Merge staging-next into staging
This commit is contained in:
		
						commit
						c4e30cf98c
					
				@ -326,6 +326,8 @@ rec {
 | 
			
		||||
 | 
			
		||||
      # The value with a check that it is defined
 | 
			
		||||
      valueDefined = if res.isDefined then res.mergedValue else
 | 
			
		||||
        # (nixos-option detects this specific error message and gives it special
 | 
			
		||||
        # handling.  If changed here, please change it there too.)
 | 
			
		||||
        throw "The option `${showOption loc}' is used but not defined.";
 | 
			
		||||
 | 
			
		||||
      # Apply the 'apply' function to the merged value. This allows options to
 | 
			
		||||
 | 
			
		||||
@ -1194,6 +1194,12 @@
 | 
			
		||||
    githubId = 30435868;
 | 
			
		||||
    name = "Okina Matara";
 | 
			
		||||
  };
 | 
			
		||||
  chkno = {
 | 
			
		||||
    email = "chuck@intelligence.org";
 | 
			
		||||
    github = "chkno";
 | 
			
		||||
    githubId = 1118859;
 | 
			
		||||
    name = "Scott Worley";
 | 
			
		||||
  };
 | 
			
		||||
  choochootrain = {
 | 
			
		||||
    email = "hurshal@imap.cc";
 | 
			
		||||
    github = "choochootrain";
 | 
			
		||||
@ -1563,6 +1569,12 @@
 | 
			
		||||
    githubId = 14032;
 | 
			
		||||
    name = "Daniel Brockman";
 | 
			
		||||
  };
 | 
			
		||||
  dduan = {
 | 
			
		||||
    email = "daniel@duan.ca";
 | 
			
		||||
    github = "dduan";
 | 
			
		||||
    githubId = 75067;
 | 
			
		||||
    name = "Daniel Duan";
 | 
			
		||||
  };
 | 
			
		||||
  deepfire = {
 | 
			
		||||
    email = "_deepfire@feelingofgreen.ru";
 | 
			
		||||
    github = "deepfire";
 | 
			
		||||
@ -2030,6 +2042,12 @@
 | 
			
		||||
      github = "ericnorris";
 | 
			
		||||
      githubId = 1906605;
 | 
			
		||||
  };
 | 
			
		||||
  Enteee = {
 | 
			
		||||
    email = "nix@duckpond.ch";
 | 
			
		||||
    github = "Enteee";
 | 
			
		||||
    githubid = 5493775;
 | 
			
		||||
    name = "Ente";
 | 
			
		||||
  };
 | 
			
		||||
  enzime = {
 | 
			
		||||
    email = "enzime@users.noreply.github.com";
 | 
			
		||||
    github = "enzime";
 | 
			
		||||
@ -7075,6 +7093,12 @@
 | 
			
		||||
    email = "kirill.wedens@gmail.com";
 | 
			
		||||
    name = "wedens";
 | 
			
		||||
  };
 | 
			
		||||
  WhittlesJr = {
 | 
			
		||||
    email = "alex.joseph.whitt@gmail.com";
 | 
			
		||||
    github = "WhittlesJr";
 | 
			
		||||
    githubId = 19174984;
 | 
			
		||||
    name = "Alex Whitt";
 | 
			
		||||
  };
 | 
			
		||||
  willibutz = {
 | 
			
		||||
    email = "willibutz@posteo.de";
 | 
			
		||||
    github = "willibutz";
 | 
			
		||||
 | 
			
		||||
@ -14,14 +14,14 @@
 | 
			
		||||
starting VDE switch for network 1
 | 
			
		||||
<prompt>></prompt>
 | 
			
		||||
</screen>
 | 
			
		||||
  You can then take any Perl statement, e.g.
 | 
			
		||||
  You can then take any Python statement, e.g.
 | 
			
		||||
<screen>
 | 
			
		||||
<prompt>></prompt> startAll
 | 
			
		||||
<prompt>></prompt> testScript
 | 
			
		||||
<prompt>></prompt> $machine->succeed("touch /tmp/foo")
 | 
			
		||||
<prompt>></prompt> print($machine->succeed("pwd")) # Show stdout of command
 | 
			
		||||
<prompt>></prompt> start_all()
 | 
			
		||||
<prompt>></prompt> test_script()
 | 
			
		||||
<prompt>></prompt> machine.succeed("touch /tmp/foo")
 | 
			
		||||
<prompt>></prompt> print(machine.succeed("pwd")) # Show stdout of command
 | 
			
		||||
</screen>
 | 
			
		||||
  The function <command>testScript</command> executes the entire test script
 | 
			
		||||
  The function <command>test_script</command> executes the entire test script
 | 
			
		||||
  and drops you back into the test driver command line upon its completion.
 | 
			
		||||
  This allows you to inspect the state of the VMs after the test (e.g. to debug
 | 
			
		||||
  the test script).
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@
 | 
			
		||||
 <para>
 | 
			
		||||
  A NixOS test is a Nix expression that has the following structure:
 | 
			
		||||
<programlisting>
 | 
			
		||||
import ./make-test.nix {
 | 
			
		||||
import ./make-test-python.nix {
 | 
			
		||||
 | 
			
		||||
  # Either the configuration of a single machine:
 | 
			
		||||
  machine =
 | 
			
		||||
@ -27,11 +27,11 @@ import ./make-test.nix {
 | 
			
		||||
 | 
			
		||||
  testScript =
 | 
			
		||||
    ''
 | 
			
		||||
      <replaceable>Perl code…</replaceable>
 | 
			
		||||
      <replaceable>Python code…</replaceable>
 | 
			
		||||
    '';
 | 
			
		||||
}
 | 
			
		||||
</programlisting>
 | 
			
		||||
  The attribute <literal>testScript</literal> is a bit of Perl code that
 | 
			
		||||
  The attribute <literal>testScript</literal> is a bit of Python code that
 | 
			
		||||
  executes the test (described below). During the test, it will start one or
 | 
			
		||||
  more virtual machines, the configuration of which is described by the
 | 
			
		||||
  attribute <literal>machine</literal> (if you need only one machine in your
 | 
			
		||||
@ -96,26 +96,27 @@ xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/virtualis
 | 
			
		||||
 </para>
 | 
			
		||||
 | 
			
		||||
 <para>
 | 
			
		||||
  The test script is a sequence of Perl statements that perform various
 | 
			
		||||
  The test script is a sequence of Python statements that perform various
 | 
			
		||||
  actions, such as starting VMs, executing commands in the VMs, and so on. Each
 | 
			
		||||
  virtual machine is represented as an object stored in the variable
 | 
			
		||||
  <literal>$<replaceable>name</replaceable></literal>, where
 | 
			
		||||
  <replaceable>name</replaceable> is the identifier of the machine (which is
 | 
			
		||||
  just <literal>machine</literal> if you didn’t specify multiple machines
 | 
			
		||||
  using the <literal>nodes</literal> attribute). For instance, the following
 | 
			
		||||
  starts the machine, waits until it has finished booting, then executes a
 | 
			
		||||
  command and checks that the output is more-or-less correct:
 | 
			
		||||
  <literal><replaceable>name</replaceable></literal> if this is also the
 | 
			
		||||
  identifier of the machine in the declarative config.
 | 
			
		||||
  If you didn't specify multiple machines using the <literal>nodes</literal>
 | 
			
		||||
  attribute, it is just <literal>machine</literal>.
 | 
			
		||||
  The following example starts the machine, waits until it has finished booting,
 | 
			
		||||
  then executes a command and checks that the output is more-or-less correct:
 | 
			
		||||
<programlisting>
 | 
			
		||||
$machine->start;
 | 
			
		||||
$machine->waitForUnit("default.target");
 | 
			
		||||
$machine->succeed("uname") =~ /Linux/ or die;
 | 
			
		||||
machine.start()
 | 
			
		||||
machine.wait_for_unit("default.target")
 | 
			
		||||
if not "Linux" in machine.succeed("uname"):
 | 
			
		||||
  raise Exception("Wrong OS")
 | 
			
		||||
</programlisting>
 | 
			
		||||
  The first line is actually unnecessary; machines are implicitly started when
 | 
			
		||||
  you first execute an action on them (such as <literal>waitForUnit</literal>
 | 
			
		||||
  you first execute an action on them (such as <literal>wait_for_unit</literal>
 | 
			
		||||
  or <literal>succeed</literal>). If you have multiple machines, you can speed
 | 
			
		||||
  up the test by starting them in parallel:
 | 
			
		||||
<programlisting>
 | 
			
		||||
startAll;
 | 
			
		||||
start_all()
 | 
			
		||||
</programlisting>
 | 
			
		||||
 </para>
 | 
			
		||||
 | 
			
		||||
@ -187,7 +188,7 @@ startAll;
 | 
			
		||||
   </varlistentry>
 | 
			
		||||
   <varlistentry>
 | 
			
		||||
    <term>
 | 
			
		||||
     <methodname>getScreenText</methodname>
 | 
			
		||||
     <methodname>get_screen_text</methodname>
 | 
			
		||||
    </term>
 | 
			
		||||
    <listitem>
 | 
			
		||||
     <para>
 | 
			
		||||
@ -204,7 +205,7 @@ startAll;
 | 
			
		||||
   </varlistentry>
 | 
			
		||||
   <varlistentry>
 | 
			
		||||
    <term>
 | 
			
		||||
     <methodname>sendMonitorCommand</methodname>
 | 
			
		||||
     <methodname>send_monitor_command</methodname>
 | 
			
		||||
    </term>
 | 
			
		||||
    <listitem>
 | 
			
		||||
     <para>
 | 
			
		||||
@ -215,23 +216,23 @@ startAll;
 | 
			
		||||
   </varlistentry>
 | 
			
		||||
   <varlistentry>
 | 
			
		||||
    <term>
 | 
			
		||||
     <methodname>sendKeys</methodname>
 | 
			
		||||
     <methodname>send_keys</methodname>
 | 
			
		||||
    </term>
 | 
			
		||||
    <listitem>
 | 
			
		||||
     <para>
 | 
			
		||||
      Simulate pressing keys on the virtual keyboard, e.g.,
 | 
			
		||||
      <literal>sendKeys("ctrl-alt-delete")</literal>.
 | 
			
		||||
      <literal>send_keys("ctrl-alt-delete")</literal>.
 | 
			
		||||
     </para>
 | 
			
		||||
    </listitem>
 | 
			
		||||
   </varlistentry>
 | 
			
		||||
   <varlistentry>
 | 
			
		||||
    <term>
 | 
			
		||||
     <methodname>sendChars</methodname>
 | 
			
		||||
     <methodname>send_chars</methodname>
 | 
			
		||||
    </term>
 | 
			
		||||
    <listitem>
 | 
			
		||||
     <para>
 | 
			
		||||
      Simulate typing a sequence of characters on the virtual keyboard, e.g.,
 | 
			
		||||
      <literal>sendKeys("foobar\n")</literal> will type the string
 | 
			
		||||
      <literal>send_keys("foobar\n")</literal> will type the string
 | 
			
		||||
      <literal>foobar</literal> followed by the Enter key.
 | 
			
		||||
     </para>
 | 
			
		||||
    </listitem>
 | 
			
		||||
@ -272,7 +273,7 @@ startAll;
 | 
			
		||||
   </varlistentry>
 | 
			
		||||
   <varlistentry>
 | 
			
		||||
    <term>
 | 
			
		||||
     <methodname>waitUntilSucceeds</methodname>
 | 
			
		||||
     <methodname>wait_until_succeeds</methodname>
 | 
			
		||||
    </term>
 | 
			
		||||
    <listitem>
 | 
			
		||||
     <para>
 | 
			
		||||
@ -282,7 +283,7 @@ startAll;
 | 
			
		||||
   </varlistentry>
 | 
			
		||||
   <varlistentry>
 | 
			
		||||
    <term>
 | 
			
		||||
     <methodname>waitUntilFails</methodname>
 | 
			
		||||
     <methodname>wait_until_fails</methodname>
 | 
			
		||||
    </term>
 | 
			
		||||
    <listitem>
 | 
			
		||||
     <para>
 | 
			
		||||
@ -292,7 +293,7 @@ startAll;
 | 
			
		||||
   </varlistentry>
 | 
			
		||||
   <varlistentry>
 | 
			
		||||
    <term>
 | 
			
		||||
     <methodname>waitForUnit</methodname>
 | 
			
		||||
     <methodname>wait_for_unit</methodname>
 | 
			
		||||
    </term>
 | 
			
		||||
    <listitem>
 | 
			
		||||
     <para>
 | 
			
		||||
@ -302,7 +303,7 @@ startAll;
 | 
			
		||||
   </varlistentry>
 | 
			
		||||
   <varlistentry>
 | 
			
		||||
    <term>
 | 
			
		||||
     <methodname>waitForFile</methodname>
 | 
			
		||||
     <methodname>wait_for_file</methodname>
 | 
			
		||||
    </term>
 | 
			
		||||
    <listitem>
 | 
			
		||||
     <para>
 | 
			
		||||
@ -312,7 +313,7 @@ startAll;
 | 
			
		||||
   </varlistentry>
 | 
			
		||||
   <varlistentry>
 | 
			
		||||
    <term>
 | 
			
		||||
     <methodname>waitForOpenPort</methodname>
 | 
			
		||||
     <methodname>wait_for_open_port</methodname>
 | 
			
		||||
    </term>
 | 
			
		||||
    <listitem>
 | 
			
		||||
     <para>
 | 
			
		||||
@ -323,7 +324,7 @@ startAll;
 | 
			
		||||
   </varlistentry>
 | 
			
		||||
   <varlistentry>
 | 
			
		||||
    <term>
 | 
			
		||||
     <methodname>waitForClosedPort</methodname>
 | 
			
		||||
     <methodname>wait_for_closed_port</methodname>
 | 
			
		||||
    </term>
 | 
			
		||||
    <listitem>
 | 
			
		||||
     <para>
 | 
			
		||||
@ -333,7 +334,7 @@ startAll;
 | 
			
		||||
   </varlistentry>
 | 
			
		||||
   <varlistentry>
 | 
			
		||||
    <term>
 | 
			
		||||
     <methodname>waitForX</methodname>
 | 
			
		||||
     <methodname>wait_for_x</methodname>
 | 
			
		||||
    </term>
 | 
			
		||||
    <listitem>
 | 
			
		||||
     <para>
 | 
			
		||||
@ -343,13 +344,13 @@ startAll;
 | 
			
		||||
   </varlistentry>
 | 
			
		||||
   <varlistentry>
 | 
			
		||||
    <term>
 | 
			
		||||
     <methodname>waitForText</methodname>
 | 
			
		||||
     <methodname>wait_for_text</methodname>
 | 
			
		||||
    </term>
 | 
			
		||||
    <listitem>
 | 
			
		||||
     <para>
 | 
			
		||||
      Wait until the supplied regular expressions matches the textual contents
 | 
			
		||||
      of the screen by using optical character recognition (see
 | 
			
		||||
      <methodname>getScreenText</methodname>).
 | 
			
		||||
      <methodname>get_screen_text</methodname>).
 | 
			
		||||
     </para>
 | 
			
		||||
     <note>
 | 
			
		||||
      <para>
 | 
			
		||||
@ -361,23 +362,23 @@ startAll;
 | 
			
		||||
   </varlistentry>
 | 
			
		||||
   <varlistentry>
 | 
			
		||||
    <term>
 | 
			
		||||
     <methodname>waitForWindow</methodname>
 | 
			
		||||
     <methodname>wait_for_window</methodname>
 | 
			
		||||
    </term>
 | 
			
		||||
    <listitem>
 | 
			
		||||
     <para>
 | 
			
		||||
      Wait until an X11 window has appeared whose name matches the given
 | 
			
		||||
      regular expression, e.g., <literal>waitForWindow(qr/Terminal/)</literal>.
 | 
			
		||||
      regular expression, e.g., <literal>wait_for_window("Terminal")</literal>.
 | 
			
		||||
     </para>
 | 
			
		||||
    </listitem>
 | 
			
		||||
   </varlistentry>
 | 
			
		||||
   <varlistentry>
 | 
			
		||||
    <term>
 | 
			
		||||
     <methodname>copyFileFromHost</methodname>
 | 
			
		||||
     <methodname>copy_file_from_host</methodname>
 | 
			
		||||
    </term>
 | 
			
		||||
    <listitem>
 | 
			
		||||
     <para>
 | 
			
		||||
      Copies a file from host to machine, e.g.,
 | 
			
		||||
      <literal>copyFileFromHost("myfile", "/etc/my/important/file")</literal>.
 | 
			
		||||
      <literal>copy_file_from_host("myfile", "/etc/my/important/file")</literal>.
 | 
			
		||||
     </para>
 | 
			
		||||
     <para>
 | 
			
		||||
      The first argument is the file on the host. The file needs to be
 | 
			
		||||
@ -397,8 +398,8 @@ startAll;
 | 
			
		||||
     </para>
 | 
			
		||||
     <para>
 | 
			
		||||
<programlisting>
 | 
			
		||||
$machine->systemctl("list-jobs --no-pager"); // runs `systemctl list-jobs --no-pager`
 | 
			
		||||
$machine->systemctl("list-jobs --no-pager", "any-user"); // spawns a shell for `any-user` and runs `systemctl --user list-jobs --no-pager`
 | 
			
		||||
machine.systemctl("list-jobs --no-pager") # runs `systemctl list-jobs --no-pager`
 | 
			
		||||
machine.systemctl("list-jobs --no-pager", "any-user") # spawns a shell for `any-user` and runs `systemctl --user list-jobs --no-pager`
 | 
			
		||||
</programlisting>
 | 
			
		||||
     </para>
 | 
			
		||||
    </listitem>
 | 
			
		||||
@ -408,14 +409,14 @@ $machine->systemctl("list-jobs --no-pager", "any-user"); // spawns a shell for `
 | 
			
		||||
 | 
			
		||||
 <para>
 | 
			
		||||
  To test user units declared by <literal>systemd.user.services</literal> the
 | 
			
		||||
  optional <literal>$user</literal> argument can be used:
 | 
			
		||||
  optional <literal>user</literal> argument can be used:
 | 
			
		||||
<programlisting>
 | 
			
		||||
$machine->start;
 | 
			
		||||
$machine->waitForX;
 | 
			
		||||
$machine->waitForUnit("xautolock.service", "x-session-user");
 | 
			
		||||
machine.start()
 | 
			
		||||
machine.wait_for_x()
 | 
			
		||||
machine.wait_for_unit("xautolock.service", "x-session-user")
 | 
			
		||||
</programlisting>
 | 
			
		||||
  This applies to <literal>systemctl</literal>, <literal>getUnitInfo</literal>,
 | 
			
		||||
  <literal>waitForUnit</literal>, <literal>startJob</literal> and
 | 
			
		||||
  <literal>stopJob</literal>.
 | 
			
		||||
  This applies to <literal>systemctl</literal>, <literal>get_unit_info</literal>,
 | 
			
		||||
  <literal>wait_for_unit</literal>, <literal>start_job</literal> and
 | 
			
		||||
  <literal>stop_job</literal>.
 | 
			
		||||
 </para>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
@ -19,14 +19,10 @@
 | 
			
		||||
   </arg>
 | 
			
		||||
 | 
			
		||||
   <arg>
 | 
			
		||||
    <option>--verbose</option>
 | 
			
		||||
    <option>--all</option>
 | 
			
		||||
   </arg>
 | 
			
		||||
 | 
			
		||||
   <arg>
 | 
			
		||||
    <option>--xml</option>
 | 
			
		||||
   </arg>
 | 
			
		||||
 | 
			
		||||
   <arg choice="plain">
 | 
			
		||||
    <replaceable>option.name</replaceable>
 | 
			
		||||
   </arg>
 | 
			
		||||
  </cmdsynopsis>
 | 
			
		||||
@ -62,22 +58,11 @@
 | 
			
		||||
   </varlistentry>
 | 
			
		||||
   <varlistentry>
 | 
			
		||||
    <term>
 | 
			
		||||
     <option>--verbose</option>
 | 
			
		||||
     <option>--all</option>
 | 
			
		||||
    </term>
 | 
			
		||||
    <listitem>
 | 
			
		||||
     <para>
 | 
			
		||||
      This option enables verbose mode, which currently is just the Bash
 | 
			
		||||
      <command>set</command> <option>-x</option> debug mode.
 | 
			
		||||
     </para>
 | 
			
		||||
    </listitem>
 | 
			
		||||
   </varlistentry>
 | 
			
		||||
   <varlistentry>
 | 
			
		||||
    <term>
 | 
			
		||||
     <option>--xml</option>
 | 
			
		||||
    </term>
 | 
			
		||||
    <listitem>
 | 
			
		||||
     <para>
 | 
			
		||||
      This option causes the output to be rendered as XML.
 | 
			
		||||
      Print the values of all options.
 | 
			
		||||
     </para>
 | 
			
		||||
    </listitem>
 | 
			
		||||
   </varlistentry>
 | 
			
		||||
 | 
			
		||||
@ -49,6 +49,12 @@
 | 
			
		||||
       zfs as soon as any zfs mountpoint is configured in <varname>fileSystems</varname>.
 | 
			
		||||
     </para>
 | 
			
		||||
   </listitem>
 | 
			
		||||
   <listitem>
 | 
			
		||||
    <para>
 | 
			
		||||
      <command>nixos-option</command> has been rewritten in C++, speeding it up, improving correctness,
 | 
			
		||||
      and adding a <option>--all</option> option which prints all options and their values.
 | 
			
		||||
    </para>
 | 
			
		||||
   </listitem>
 | 
			
		||||
  </itemizedlist>
 | 
			
		||||
 </section>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										758
									
								
								nixos/lib/test-driver/test-driver.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										758
									
								
								nixos/lib/test-driver/test-driver.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,758 @@
 | 
			
		||||
#! /somewhere/python3
 | 
			
		||||
 | 
			
		||||
from contextlib import contextmanager
 | 
			
		||||
from xml.sax.saxutils import XMLGenerator
 | 
			
		||||
import _thread
 | 
			
		||||
import atexit
 | 
			
		||||
import os
 | 
			
		||||
import pty
 | 
			
		||||
import queue
 | 
			
		||||
import re
 | 
			
		||||
import shutil
 | 
			
		||||
import socket
 | 
			
		||||
import subprocess
 | 
			
		||||
import sys
 | 
			
		||||
import tempfile
 | 
			
		||||
import time
 | 
			
		||||
import unicodedata
 | 
			
		||||
import ptpython.repl
 | 
			
		||||
 | 
			
		||||
CHAR_TO_KEY = {
 | 
			
		||||
    "A": "shift-a",
 | 
			
		||||
    "N": "shift-n",
 | 
			
		||||
    "-": "0x0C",
 | 
			
		||||
    "_": "shift-0x0C",
 | 
			
		||||
    "B": "shift-b",
 | 
			
		||||
    "O": "shift-o",
 | 
			
		||||
    "=": "0x0D",
 | 
			
		||||
    "+": "shift-0x0D",
 | 
			
		||||
    "C": "shift-c",
 | 
			
		||||
    "P": "shift-p",
 | 
			
		||||
    "[": "0x1A",
 | 
			
		||||
    "{": "shift-0x1A",
 | 
			
		||||
    "D": "shift-d",
 | 
			
		||||
    "Q": "shift-q",
 | 
			
		||||
    "]": "0x1B",
 | 
			
		||||
    "}": "shift-0x1B",
 | 
			
		||||
    "E": "shift-e",
 | 
			
		||||
    "R": "shift-r",
 | 
			
		||||
    ";": "0x27",
 | 
			
		||||
    ":": "shift-0x27",
 | 
			
		||||
    "F": "shift-f",
 | 
			
		||||
    "S": "shift-s",
 | 
			
		||||
    "'": "0x28",
 | 
			
		||||
    '"': "shift-0x28",
 | 
			
		||||
    "G": "shift-g",
 | 
			
		||||
    "T": "shift-t",
 | 
			
		||||
    "`": "0x29",
 | 
			
		||||
    "~": "shift-0x29",
 | 
			
		||||
    "H": "shift-h",
 | 
			
		||||
    "U": "shift-u",
 | 
			
		||||
    "\\": "0x2B",
 | 
			
		||||
    "|": "shift-0x2B",
 | 
			
		||||
    "I": "shift-i",
 | 
			
		||||
    "V": "shift-v",
 | 
			
		||||
    ",": "0x33",
 | 
			
		||||
    "<": "shift-0x33",
 | 
			
		||||
    "J": "shift-j",
 | 
			
		||||
    "W": "shift-w",
 | 
			
		||||
    ".": "0x34",
 | 
			
		||||
    ">": "shift-0x34",
 | 
			
		||||
    "K": "shift-k",
 | 
			
		||||
    "X": "shift-x",
 | 
			
		||||
    "/": "0x35",
 | 
			
		||||
    "?": "shift-0x35",
 | 
			
		||||
    "L": "shift-l",
 | 
			
		||||
    "Y": "shift-y",
 | 
			
		||||
    " ": "spc",
 | 
			
		||||
    "M": "shift-m",
 | 
			
		||||
    "Z": "shift-z",
 | 
			
		||||
    "\n": "ret",
 | 
			
		||||
    "!": "shift-0x02",
 | 
			
		||||
    "@": "shift-0x03",
 | 
			
		||||
    "#": "shift-0x04",
 | 
			
		||||
    "$": "shift-0x05",
 | 
			
		||||
    "%": "shift-0x06",
 | 
			
		||||
    "^": "shift-0x07",
 | 
			
		||||
    "&": "shift-0x08",
 | 
			
		||||
    "*": "shift-0x09",
 | 
			
		||||
    "(": "shift-0x0A",
 | 
			
		||||
    ")": "shift-0x0B",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def eprint(*args, **kwargs):
 | 
			
		||||
    print(*args, file=sys.stderr, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_vlan(vlan_nr):
 | 
			
		||||
    global log
 | 
			
		||||
    log.log("starting VDE switch for network {}".format(vlan_nr))
 | 
			
		||||
    vde_socket = os.path.abspath("./vde{}.ctl".format(vlan_nr))
 | 
			
		||||
    pty_master, pty_slave = pty.openpty()
 | 
			
		||||
    vde_process = subprocess.Popen(
 | 
			
		||||
        ["vde_switch", "-s", vde_socket, "--dirmode", "0777"],
 | 
			
		||||
        bufsize=1,
 | 
			
		||||
        stdin=pty_slave,
 | 
			
		||||
        stdout=subprocess.PIPE,
 | 
			
		||||
        stderr=subprocess.PIPE,
 | 
			
		||||
        shell=False,
 | 
			
		||||
    )
 | 
			
		||||
    fd = os.fdopen(pty_master, "w")
 | 
			
		||||
    fd.write("version\n")
 | 
			
		||||
    # TODO: perl version checks if this can be read from
 | 
			
		||||
    # an if not, dies. we could hang here forever. Fix it.
 | 
			
		||||
    vde_process.stdout.readline()
 | 
			
		||||
    if not os.path.exists(os.path.join(vde_socket, "ctl")):
 | 
			
		||||
        raise Exception("cannot start vde_switch")
 | 
			
		||||
 | 
			
		||||
    return (vlan_nr, vde_socket, vde_process, fd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def retry(fn):
 | 
			
		||||
    """Call the given function repeatedly, with 1 second intervals,
 | 
			
		||||
    until it returns True or a timeout is reached.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    for _ in range(900):
 | 
			
		||||
        if fn(False):
 | 
			
		||||
            return
 | 
			
		||||
        time.sleep(1)
 | 
			
		||||
 | 
			
		||||
    if not fn(True):
 | 
			
		||||
        raise Exception("action timed out")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Logger:
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.logfile = os.environ.get("LOGFILE", "/dev/null")
 | 
			
		||||
        self.logfile_handle = open(self.logfile, "wb")
 | 
			
		||||
        self.xml = XMLGenerator(self.logfile_handle, encoding="utf-8")
 | 
			
		||||
        self.queue = queue.Queue(1000)
 | 
			
		||||
 | 
			
		||||
        self.xml.startDocument()
 | 
			
		||||
        self.xml.startElement("logfile", attrs={})
 | 
			
		||||
 | 
			
		||||
    def close(self):
 | 
			
		||||
        self.xml.endElement("logfile")
 | 
			
		||||
        self.xml.endDocument()
 | 
			
		||||
        self.logfile_handle.close()
 | 
			
		||||
 | 
			
		||||
    def sanitise(self, message):
 | 
			
		||||
        return "".join(ch for ch in message if unicodedata.category(ch)[0] != "C")
 | 
			
		||||
 | 
			
		||||
    def maybe_prefix(self, message, attributes):
 | 
			
		||||
        if "machine" in attributes:
 | 
			
		||||
            return "{}: {}".format(attributes["machine"], message)
 | 
			
		||||
        return message
 | 
			
		||||
 | 
			
		||||
    def log_line(self, message, attributes):
 | 
			
		||||
        self.xml.startElement("line", attributes)
 | 
			
		||||
        self.xml.characters(message)
 | 
			
		||||
        self.xml.endElement("line")
 | 
			
		||||
 | 
			
		||||
    def log(self, message, attributes={}):
 | 
			
		||||
        eprint(self.maybe_prefix(message, attributes))
 | 
			
		||||
        self.drain_log_queue()
 | 
			
		||||
        self.log_line(message, attributes)
 | 
			
		||||
 | 
			
		||||
    def enqueue(self, message):
 | 
			
		||||
        self.queue.put(message)
 | 
			
		||||
 | 
			
		||||
    def drain_log_queue(self):
 | 
			
		||||
        try:
 | 
			
		||||
            while True:
 | 
			
		||||
                item = self.queue.get_nowait()
 | 
			
		||||
                attributes = {"machine": item["machine"], "type": "serial"}
 | 
			
		||||
                self.log_line(self.sanitise(item["msg"]), attributes)
 | 
			
		||||
        except queue.Empty:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    @contextmanager
 | 
			
		||||
    def nested(self, message, attributes={}):
 | 
			
		||||
        eprint(self.maybe_prefix(message, attributes))
 | 
			
		||||
 | 
			
		||||
        self.xml.startElement("nest", attrs={})
 | 
			
		||||
        self.xml.startElement("head", attributes)
 | 
			
		||||
        self.xml.characters(message)
 | 
			
		||||
        self.xml.endElement("head")
 | 
			
		||||
 | 
			
		||||
        tic = time.time()
 | 
			
		||||
        self.drain_log_queue()
 | 
			
		||||
        yield
 | 
			
		||||
        self.drain_log_queue()
 | 
			
		||||
        toc = time.time()
 | 
			
		||||
        self.log("({:.2f} seconds)".format(toc - tic))
 | 
			
		||||
 | 
			
		||||
        self.xml.endElement("nest")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Machine:
 | 
			
		||||
    def __init__(self, args):
 | 
			
		||||
        if "name" in args:
 | 
			
		||||
            self.name = args["name"]
 | 
			
		||||
        else:
 | 
			
		||||
            self.name = "machine"
 | 
			
		||||
            try:
 | 
			
		||||
                cmd = args["startCommand"]
 | 
			
		||||
                self.name = re.search("run-(.+)-vm$", cmd).group(1)
 | 
			
		||||
            except KeyError:
 | 
			
		||||
                pass
 | 
			
		||||
            except AttributeError:
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
        self.script = args.get("startCommand", self.create_startcommand(args))
 | 
			
		||||
 | 
			
		||||
        tmp_dir = os.environ.get("TMPDIR", tempfile.gettempdir())
 | 
			
		||||
 | 
			
		||||
        def create_dir(name):
 | 
			
		||||
            path = os.path.join(tmp_dir, name)
 | 
			
		||||
            os.makedirs(path, mode=0o700, exist_ok=True)
 | 
			
		||||
            return path
 | 
			
		||||
 | 
			
		||||
        self.state_dir = create_dir("vm-state-{}".format(self.name))
 | 
			
		||||
        self.shared_dir = create_dir("xchg-shared")
 | 
			
		||||
 | 
			
		||||
        self.booted = False
 | 
			
		||||
        self.connected = False
 | 
			
		||||
        self.pid = None
 | 
			
		||||
        self.socket = None
 | 
			
		||||
        self.monitor = None
 | 
			
		||||
        self.logger = args["log"]
 | 
			
		||||
        self.allow_reboot = args.get("allowReboot", False)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def create_startcommand(args):
 | 
			
		||||
        net_backend = "-netdev user,id=net0"
 | 
			
		||||
        net_frontend = "-device virtio-net-pci,netdev=net0"
 | 
			
		||||
 | 
			
		||||
        if "netBackendArgs" in args:
 | 
			
		||||
            net_backend += "," + args["netBackendArgs"]
 | 
			
		||||
 | 
			
		||||
        if "netFrontendArgs" in args:
 | 
			
		||||
            net_frontend += "," + args["netFrontendArgs"]
 | 
			
		||||
 | 
			
		||||
        start_command = (
 | 
			
		||||
            "qemu-kvm -m 384 " + net_backend + " " + net_frontend + " $QEMU_OPTS "
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        if "hda" in args:
 | 
			
		||||
            hda_path = os.path.abspath(args["hda"])
 | 
			
		||||
            if args.get("hdaInterface", "") == "scsi":
 | 
			
		||||
                start_command += (
 | 
			
		||||
                    "-drive id=hda,file="
 | 
			
		||||
                    + hda_path
 | 
			
		||||
                    + ",werror=report,if=none "
 | 
			
		||||
                    + "-device scsi-hd,drive=hda "
 | 
			
		||||
                )
 | 
			
		||||
            else:
 | 
			
		||||
                start_command += (
 | 
			
		||||
                    "-drive file="
 | 
			
		||||
                    + hda_path
 | 
			
		||||
                    + ",if="
 | 
			
		||||
                    + args["hdaInterface"]
 | 
			
		||||
                    + ",werror=report "
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
        if "cdrom" in args:
 | 
			
		||||
            start_command += "-cdrom " + args["cdrom"] + " "
 | 
			
		||||
 | 
			
		||||
        if "usb" in args:
 | 
			
		||||
            start_command += (
 | 
			
		||||
                "-device piix3-usb-uhci -drive "
 | 
			
		||||
                + "id=usbdisk,file="
 | 
			
		||||
                + args["usb"]
 | 
			
		||||
                + ",if=none,readonly "
 | 
			
		||||
                + "-device usb-storage,drive=usbdisk "
 | 
			
		||||
            )
 | 
			
		||||
        if "bios" in args:
 | 
			
		||||
            start_command += "-bios " + args["bios"] + " "
 | 
			
		||||
 | 
			
		||||
        start_command += args.get("qemuFlags", "")
 | 
			
		||||
 | 
			
		||||
        return start_command
 | 
			
		||||
 | 
			
		||||
    def is_up(self):
 | 
			
		||||
        return self.booted and self.connected
 | 
			
		||||
 | 
			
		||||
    def log(self, msg):
 | 
			
		||||
        self.logger.log(msg, {"machine": self.name})
 | 
			
		||||
 | 
			
		||||
    def nested(self, msg, attrs={}):
 | 
			
		||||
        my_attrs = {"machine": self.name}
 | 
			
		||||
        my_attrs.update(attrs)
 | 
			
		||||
        return self.logger.nested(msg, my_attrs)
 | 
			
		||||
 | 
			
		||||
    def wait_for_monitor_prompt(self):
 | 
			
		||||
        while True:
 | 
			
		||||
            answer = self.monitor.recv(1024).decode()
 | 
			
		||||
            if answer.endswith("(qemu) "):
 | 
			
		||||
                return answer
 | 
			
		||||
 | 
			
		||||
    def send_monitor_command(self, command):
 | 
			
		||||
        message = ("{}\n".format(command)).encode()
 | 
			
		||||
        self.log("sending monitor command: {}".format(command))
 | 
			
		||||
        self.monitor.send(message)
 | 
			
		||||
        return self.wait_for_monitor_prompt()
 | 
			
		||||
 | 
			
		||||
    def wait_for_unit(self, unit, user=None):
 | 
			
		||||
        while True:
 | 
			
		||||
            info = self.get_unit_info(unit, user)
 | 
			
		||||
            state = info["ActiveState"]
 | 
			
		||||
            if state == "failed":
 | 
			
		||||
                raise Exception('unit "{}" reached state "{}"'.format(unit, state))
 | 
			
		||||
 | 
			
		||||
            if state == "inactive":
 | 
			
		||||
                status, jobs = self.systemctl("list-jobs --full 2>&1", user)
 | 
			
		||||
                if "No jobs" in jobs:
 | 
			
		||||
                    info = self.get_unit_info(unit)
 | 
			
		||||
                    if info["ActiveState"] == state:
 | 
			
		||||
                        raise Exception(
 | 
			
		||||
                            (
 | 
			
		||||
                                'unit "{}" is inactive and there ' "are no pending jobs"
 | 
			
		||||
                            ).format(unit)
 | 
			
		||||
                        )
 | 
			
		||||
            if state == "active":
 | 
			
		||||
                return True
 | 
			
		||||
 | 
			
		||||
    def get_unit_info(self, unit, user=None):
 | 
			
		||||
        status, lines = self.systemctl('--no-pager show "{}"'.format(unit), user)
 | 
			
		||||
        if status != 0:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
        line_pattern = re.compile(r"^([^=]+)=(.*)$")
 | 
			
		||||
 | 
			
		||||
        def tuple_from_line(line):
 | 
			
		||||
            match = line_pattern.match(line)
 | 
			
		||||
            return match[1], match[2]
 | 
			
		||||
 | 
			
		||||
        return dict(
 | 
			
		||||
            tuple_from_line(line)
 | 
			
		||||
            for line in lines.split("\n")
 | 
			
		||||
            if line_pattern.match(line)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def systemctl(self, q, user=None):
 | 
			
		||||
        if user is not None:
 | 
			
		||||
            q = q.replace("'", "\\'")
 | 
			
		||||
            return self.execute(
 | 
			
		||||
                (
 | 
			
		||||
                    "su -l {} -c "
 | 
			
		||||
                    "$'XDG_RUNTIME_DIR=/run/user/`id -u` "
 | 
			
		||||
                    "systemctl --user {}'"
 | 
			
		||||
                ).format(user, q)
 | 
			
		||||
            )
 | 
			
		||||
        return self.execute("systemctl {}".format(q))
 | 
			
		||||
 | 
			
		||||
    def execute(self, command):
 | 
			
		||||
        self.connect()
 | 
			
		||||
 | 
			
		||||
        out_command = "( {} ); echo '|!EOF' $?\n".format(command)
 | 
			
		||||
        self.shell.send(out_command.encode())
 | 
			
		||||
 | 
			
		||||
        output = ""
 | 
			
		||||
        status_code_pattern = re.compile(r"(.*)\|\!EOF\s+(\d+)")
 | 
			
		||||
 | 
			
		||||
        while True:
 | 
			
		||||
            chunk = self.shell.recv(4096).decode()
 | 
			
		||||
            match = status_code_pattern.match(chunk)
 | 
			
		||||
            if match:
 | 
			
		||||
                output += match[1]
 | 
			
		||||
                status_code = int(match[2])
 | 
			
		||||
                return (status_code, output)
 | 
			
		||||
            output += chunk
 | 
			
		||||
 | 
			
		||||
    def succeed(self, *commands):
 | 
			
		||||
        """Execute each command and check that it succeeds."""
 | 
			
		||||
        for command in commands:
 | 
			
		||||
            with self.nested("must succeed: {}".format(command)):
 | 
			
		||||
                status, output = self.execute(command)
 | 
			
		||||
                if status != 0:
 | 
			
		||||
                    self.log("output: {}".format(output))
 | 
			
		||||
                    raise Exception(
 | 
			
		||||
                        "command `{}` failed (exit code {})".format(command, status)
 | 
			
		||||
                    )
 | 
			
		||||
                return output
 | 
			
		||||
 | 
			
		||||
    def fail(self, *commands):
 | 
			
		||||
        """Execute each command and check that it fails."""
 | 
			
		||||
        for command in commands:
 | 
			
		||||
            with self.nested("must fail: {}".format(command)):
 | 
			
		||||
                status, output = self.execute(command)
 | 
			
		||||
                if status == 0:
 | 
			
		||||
                    raise Exception(
 | 
			
		||||
                        "command `{}` unexpectedly succeeded".format(command)
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
    def wait_until_succeeds(self, command):
 | 
			
		||||
        with self.nested("waiting for success: {}".format(command)):
 | 
			
		||||
            while True:
 | 
			
		||||
                status, output = self.execute(command)
 | 
			
		||||
                if status == 0:
 | 
			
		||||
                    return output
 | 
			
		||||
 | 
			
		||||
    def wait_until_fails(self, command):
 | 
			
		||||
        with self.nested("waiting for failure: {}".format(command)):
 | 
			
		||||
            while True:
 | 
			
		||||
                status, output = self.execute(command)
 | 
			
		||||
                if status != 0:
 | 
			
		||||
                    return output
 | 
			
		||||
 | 
			
		||||
    def wait_for_shutdown(self):
 | 
			
		||||
        if not self.booted:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        with self.nested("waiting for the VM to power off"):
 | 
			
		||||
            sys.stdout.flush()
 | 
			
		||||
            self.process.wait()
 | 
			
		||||
 | 
			
		||||
            self.pid = None
 | 
			
		||||
            self.booted = False
 | 
			
		||||
            self.connected = False
 | 
			
		||||
 | 
			
		||||
    def get_tty_text(self, tty):
 | 
			
		||||
        status, output = self.execute(
 | 
			
		||||
            "fold -w$(stty -F /dev/tty{0} size | "
 | 
			
		||||
            "awk '{{print $2}}') /dev/vcs{0}".format(tty)
 | 
			
		||||
        )
 | 
			
		||||
        return output
 | 
			
		||||
 | 
			
		||||
    def wait_until_tty_matches(self, tty, regexp):
 | 
			
		||||
        matcher = re.compile(regexp)
 | 
			
		||||
        with self.nested("waiting for {} to appear on tty {}".format(regexp, tty)):
 | 
			
		||||
            while True:
 | 
			
		||||
                text = self.get_tty_text(tty)
 | 
			
		||||
                if len(matcher.findall(text)) > 0:
 | 
			
		||||
                    return True
 | 
			
		||||
 | 
			
		||||
    def send_chars(self, chars):
 | 
			
		||||
        with self.nested("sending keys ‘{}‘".format(chars)):
 | 
			
		||||
            for char in chars:
 | 
			
		||||
                self.send_key(char)
 | 
			
		||||
 | 
			
		||||
    def wait_for_file(self, filename):
 | 
			
		||||
        with self.nested("waiting for file ‘{}‘".format(filename)):
 | 
			
		||||
            while True:
 | 
			
		||||
                status, _ = self.execute("test -e {}".format(filename))
 | 
			
		||||
                if status == 0:
 | 
			
		||||
                    return True
 | 
			
		||||
 | 
			
		||||
    def wait_for_open_port(self, port):
 | 
			
		||||
        def port_is_open(_):
 | 
			
		||||
            status, _ = self.execute("nc -z localhost {}".format(port))
 | 
			
		||||
            return status == 0
 | 
			
		||||
 | 
			
		||||
        with self.nested("waiting for TCP port {}".format(port)):
 | 
			
		||||
            retry(port_is_open)
 | 
			
		||||
 | 
			
		||||
    def wait_for_closed_port(self, port):
 | 
			
		||||
        def port_is_closed(_):
 | 
			
		||||
            status, _ = self.execute("nc -z localhost {}".format(port))
 | 
			
		||||
            return status != 0
 | 
			
		||||
 | 
			
		||||
        retry(port_is_closed)
 | 
			
		||||
 | 
			
		||||
    def start_job(self, jobname, user=None):
 | 
			
		||||
        return self.systemctl("start {}".format(jobname), user)
 | 
			
		||||
 | 
			
		||||
    def stop_job(self, jobname, user=None):
 | 
			
		||||
        return self.systemctl("stop {}".format(jobname), user)
 | 
			
		||||
 | 
			
		||||
    def wait_for_job(self, jobname):
 | 
			
		||||
        return self.wait_for_unit(jobname)
 | 
			
		||||
 | 
			
		||||
    def connect(self):
 | 
			
		||||
        if self.connected:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        with self.nested("waiting for the VM to finish booting"):
 | 
			
		||||
            self.start()
 | 
			
		||||
 | 
			
		||||
            tic = time.time()
 | 
			
		||||
            self.shell.recv(1024)
 | 
			
		||||
            # TODO: Timeout
 | 
			
		||||
            toc = time.time()
 | 
			
		||||
 | 
			
		||||
            self.log("connected to guest root shell")
 | 
			
		||||
            self.log("(connecting took {:.2f} seconds)".format(toc - tic))
 | 
			
		||||
            self.connected = True
 | 
			
		||||
 | 
			
		||||
    def screenshot(self, filename):
 | 
			
		||||
        out_dir = os.environ.get("out", os.getcwd())
 | 
			
		||||
        word_pattern = re.compile(r"^\w+$")
 | 
			
		||||
        if word_pattern.match(filename):
 | 
			
		||||
            filename = os.path.join(out_dir, "{}.png".format(filename))
 | 
			
		||||
        tmp = "{}.ppm".format(filename)
 | 
			
		||||
 | 
			
		||||
        with self.nested(
 | 
			
		||||
            "making screenshot {}".format(filename),
 | 
			
		||||
            {"image": os.path.basename(filename)},
 | 
			
		||||
        ):
 | 
			
		||||
            self.send_monitor_command("screendump {}".format(tmp))
 | 
			
		||||
            ret = subprocess.run("pnmtopng {} > {}".format(tmp, filename), shell=True)
 | 
			
		||||
            os.unlink(tmp)
 | 
			
		||||
            if ret.returncode != 0:
 | 
			
		||||
                raise Exception("Cannot convert screenshot")
 | 
			
		||||
 | 
			
		||||
    def get_screen_text(self):
 | 
			
		||||
        if shutil.which("tesseract") is None:
 | 
			
		||||
            raise Exception("get_screen_text used but enableOCR is false")
 | 
			
		||||
 | 
			
		||||
        magick_args = (
 | 
			
		||||
            "-filter Catrom -density 72 -resample 300 "
 | 
			
		||||
            + "-contrast -normalize -despeckle -type grayscale "
 | 
			
		||||
            + "-sharpen 1 -posterize 3 -negate -gamma 100 "
 | 
			
		||||
            + "-blur 1x65535"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        tess_args = "-c debug_file=/dev/null --psm 11 --oem 2"
 | 
			
		||||
 | 
			
		||||
        with self.nested("performing optical character recognition"):
 | 
			
		||||
            with tempfile.NamedTemporaryFile() as tmpin:
 | 
			
		||||
                self.send_monitor_command("screendump {}".format(tmpin.name))
 | 
			
		||||
 | 
			
		||||
                cmd = "convert {} {} tiff:- | tesseract - - {}".format(
 | 
			
		||||
                    magick_args, tmpin.name, tess_args
 | 
			
		||||
                )
 | 
			
		||||
                ret = subprocess.run(cmd, shell=True, capture_output=True)
 | 
			
		||||
                if ret.returncode != 0:
 | 
			
		||||
                    raise Exception(
 | 
			
		||||
                        "OCR failed with exit code {}".format(ret.returncode)
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                return ret.stdout.decode("utf-8")
 | 
			
		||||
 | 
			
		||||
    def wait_for_text(self, regex):
 | 
			
		||||
        def screen_matches(last):
 | 
			
		||||
            text = self.get_screen_text()
 | 
			
		||||
            m = re.search(regex, text)
 | 
			
		||||
 | 
			
		||||
            if last and not m:
 | 
			
		||||
                self.log("Last OCR attempt failed. Text was: {}".format(text))
 | 
			
		||||
 | 
			
		||||
            return m
 | 
			
		||||
 | 
			
		||||
        with self.nested("waiting for {} to appear on screen".format(regex)):
 | 
			
		||||
            retry(screen_matches)
 | 
			
		||||
 | 
			
		||||
    def send_key(self, key):
 | 
			
		||||
        key = CHAR_TO_KEY.get(key, key)
 | 
			
		||||
        self.send_monitor_command("sendkey {}".format(key))
 | 
			
		||||
 | 
			
		||||
    def start(self):
 | 
			
		||||
        if self.booted:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        self.log("starting vm")
 | 
			
		||||
 | 
			
		||||
        def create_socket(path):
 | 
			
		||||
            if os.path.exists(path):
 | 
			
		||||
                os.unlink(path)
 | 
			
		||||
            s = socket.socket(family=socket.AF_UNIX, type=socket.SOCK_STREAM)
 | 
			
		||||
            s.bind(path)
 | 
			
		||||
            s.listen(1)
 | 
			
		||||
            return s
 | 
			
		||||
 | 
			
		||||
        monitor_path = os.path.join(self.state_dir, "monitor")
 | 
			
		||||
        self.monitor_socket = create_socket(monitor_path)
 | 
			
		||||
 | 
			
		||||
        shell_path = os.path.join(self.state_dir, "shell")
 | 
			
		||||
        self.shell_socket = create_socket(shell_path)
 | 
			
		||||
 | 
			
		||||
        qemu_options = (
 | 
			
		||||
            " ".join(
 | 
			
		||||
                [
 | 
			
		||||
                    "" if self.allow_reboot else "-no-reboot",
 | 
			
		||||
                    "-monitor unix:{}".format(monitor_path),
 | 
			
		||||
                    "-chardev socket,id=shell,path={}".format(shell_path),
 | 
			
		||||
                    "-device virtio-serial",
 | 
			
		||||
                    "-device virtconsole,chardev=shell",
 | 
			
		||||
                    "-device virtio-rng-pci",
 | 
			
		||||
                    "-serial stdio" if "DISPLAY" in os.environ else "-nographic",
 | 
			
		||||
                ]
 | 
			
		||||
            )
 | 
			
		||||
            + " "
 | 
			
		||||
            + os.environ.get("QEMU_OPTS", "")
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        environment = {
 | 
			
		||||
            "QEMU_OPTS": qemu_options,
 | 
			
		||||
            "SHARED_DIR": self.shared_dir,
 | 
			
		||||
            "USE_TMPDIR": "1",
 | 
			
		||||
        }
 | 
			
		||||
        environment.update(dict(os.environ))
 | 
			
		||||
 | 
			
		||||
        self.process = subprocess.Popen(
 | 
			
		||||
            self.script,
 | 
			
		||||
            bufsize=1,
 | 
			
		||||
            stdin=subprocess.DEVNULL,
 | 
			
		||||
            stdout=subprocess.PIPE,
 | 
			
		||||
            stderr=subprocess.STDOUT,
 | 
			
		||||
            shell=False,
 | 
			
		||||
            cwd=self.state_dir,
 | 
			
		||||
            env=environment,
 | 
			
		||||
        )
 | 
			
		||||
        self.monitor, _ = self.monitor_socket.accept()
 | 
			
		||||
        self.shell, _ = self.shell_socket.accept()
 | 
			
		||||
 | 
			
		||||
        def process_serial_output():
 | 
			
		||||
            for line in self.process.stdout:
 | 
			
		||||
                line = line.decode().replace("\r", "").rstrip()
 | 
			
		||||
                eprint("{} # {}".format(self.name, line))
 | 
			
		||||
                self.logger.enqueue({"msg": line, "machine": self.name})
 | 
			
		||||
 | 
			
		||||
        _thread.start_new_thread(process_serial_output, ())
 | 
			
		||||
 | 
			
		||||
        self.wait_for_monitor_prompt()
 | 
			
		||||
 | 
			
		||||
        self.pid = self.process.pid
 | 
			
		||||
        self.booted = True
 | 
			
		||||
 | 
			
		||||
        self.log("QEMU running (pid {})".format(self.pid))
 | 
			
		||||
 | 
			
		||||
    def shutdown(self):
 | 
			
		||||
        if self.booted:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        self.shell.send("poweroff\n".encode())
 | 
			
		||||
        self.wait_for_shutdown()
 | 
			
		||||
 | 
			
		||||
    def crash(self):
 | 
			
		||||
        if self.booted:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        self.log("forced crash")
 | 
			
		||||
        self.send_monitor_command("quit")
 | 
			
		||||
        self.wait_for_shutdown()
 | 
			
		||||
 | 
			
		||||
    def wait_for_x(self):
 | 
			
		||||
        """Wait until it is possible to connect to the X server.  Note that
 | 
			
		||||
        testing the existence of /tmp/.X11-unix/X0 is insufficient.
 | 
			
		||||
        """
 | 
			
		||||
        with self.nested("waiting for the X11 server"):
 | 
			
		||||
            while True:
 | 
			
		||||
                cmd = (
 | 
			
		||||
                    "journalctl -b SYSLOG_IDENTIFIER=systemd | "
 | 
			
		||||
                    + 'grep "Reached target Current graphical"'
 | 
			
		||||
                )
 | 
			
		||||
                status, _ = self.execute(cmd)
 | 
			
		||||
                if status != 0:
 | 
			
		||||
                    continue
 | 
			
		||||
                status, _ = self.execute("[ -e /tmp/.X11-unix/X0 ]")
 | 
			
		||||
                if status == 0:
 | 
			
		||||
                    return
 | 
			
		||||
 | 
			
		||||
    def sleep(self, secs):
 | 
			
		||||
        time.sleep(secs)
 | 
			
		||||
 | 
			
		||||
    def block(self):
 | 
			
		||||
        """Make the machine unreachable by shutting down eth1 (the multicast
 | 
			
		||||
        interface used to talk to the other VMs).  We keep eth0 up so that
 | 
			
		||||
        the test driver can continue to talk to the machine.
 | 
			
		||||
        """
 | 
			
		||||
        self.send_monitor_command("set_link virtio-net-pci.1 off")
 | 
			
		||||
 | 
			
		||||
    def unblock(self):
 | 
			
		||||
        """Make the machine reachable.
 | 
			
		||||
        """
 | 
			
		||||
        self.send_monitor_command("set_link virtio-net-pci.1 on")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_machine(args):
 | 
			
		||||
    global log
 | 
			
		||||
    args["log"] = log
 | 
			
		||||
    args["redirectSerial"] = os.environ.get("USE_SERIAL", "0") == "1"
 | 
			
		||||
    return Machine(args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def start_all():
 | 
			
		||||
    with log.nested("starting all VMs"):
 | 
			
		||||
        for machine in machines:
 | 
			
		||||
            machine.start()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def join_all():
 | 
			
		||||
    with log.nested("waiting for all VMs to finish"):
 | 
			
		||||
        for machine in machines:
 | 
			
		||||
            machine.wait_for_shutdown()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_script():
 | 
			
		||||
    exec(os.environ["testScript"])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def run_tests():
 | 
			
		||||
    tests = os.environ.get("tests", None)
 | 
			
		||||
    if tests is not None:
 | 
			
		||||
        with log.nested("running the VM test script"):
 | 
			
		||||
            try:
 | 
			
		||||
                exec(tests)
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                eprint("error: {}".format(str(e)))
 | 
			
		||||
                sys.exit(1)
 | 
			
		||||
    else:
 | 
			
		||||
        ptpython.repl.embed(locals(), globals())
 | 
			
		||||
 | 
			
		||||
    # TODO: Collect coverage data
 | 
			
		||||
 | 
			
		||||
    for machine in machines:
 | 
			
		||||
        if machine.is_up():
 | 
			
		||||
            machine.execute("sync")
 | 
			
		||||
 | 
			
		||||
    if nr_tests != 0:
 | 
			
		||||
        log.log("{} out of {} tests succeeded".format(nr_succeeded, nr_tests))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@contextmanager
 | 
			
		||||
def subtest(name):
 | 
			
		||||
    global nr_tests
 | 
			
		||||
    global nr_succeeded
 | 
			
		||||
 | 
			
		||||
    with log.nested(name):
 | 
			
		||||
        nr_tests += 1
 | 
			
		||||
        try:
 | 
			
		||||
            yield
 | 
			
		||||
            nr_succeeded += 1
 | 
			
		||||
            return True
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            log.log("error: {}".format(str(e)))
 | 
			
		||||
 | 
			
		||||
    return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    global log
 | 
			
		||||
    log = Logger()
 | 
			
		||||
 | 
			
		||||
    vlan_nrs = list(dict.fromkeys(os.environ["VLANS"].split()))
 | 
			
		||||
    vde_sockets = [create_vlan(v) for v in vlan_nrs]
 | 
			
		||||
    for nr, vde_socket, _, _ in vde_sockets:
 | 
			
		||||
        os.environ["QEMU_VDE_SOCKET_{}".format(nr)] = vde_socket
 | 
			
		||||
 | 
			
		||||
    vm_scripts = sys.argv[1:]
 | 
			
		||||
    machines = [create_machine({"startCommand": s}) for s in vm_scripts]
 | 
			
		||||
    machine_eval = [
 | 
			
		||||
        "{0} = machines[{1}]".format(m.name, idx) for idx, m in enumerate(machines)
 | 
			
		||||
    ]
 | 
			
		||||
    exec("\n".join(machine_eval))
 | 
			
		||||
 | 
			
		||||
    nr_tests = 0
 | 
			
		||||
    nr_succeeded = 0
 | 
			
		||||
 | 
			
		||||
    @atexit.register
 | 
			
		||||
    def clean_up():
 | 
			
		||||
        with log.nested("cleaning up"):
 | 
			
		||||
            for machine in machines:
 | 
			
		||||
                if machine.pid is None:
 | 
			
		||||
                    continue
 | 
			
		||||
                log.log("killing {} (pid {})".format(machine.name, machine.pid))
 | 
			
		||||
                machine.process.kill()
 | 
			
		||||
 | 
			
		||||
            for _, _, process, _ in vde_sockets:
 | 
			
		||||
                process.kill()
 | 
			
		||||
        log.close()
 | 
			
		||||
 | 
			
		||||
    tic = time.time()
 | 
			
		||||
    run_tests()
 | 
			
		||||
    toc = time.time()
 | 
			
		||||
    print("test script finished in {:.2f}s".format(toc - tic))
 | 
			
		||||
							
								
								
									
										279
									
								
								nixos/lib/testing-python.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								nixos/lib/testing-python.nix
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,279 @@
 | 
			
		||||
{ system
 | 
			
		||||
, pkgs ? import ../.. { inherit system config; }
 | 
			
		||||
  # Use a minimal kernel?
 | 
			
		||||
, minimal ? false
 | 
			
		||||
  # Ignored
 | 
			
		||||
, config ? {}
 | 
			
		||||
  # Modules to add to each VM
 | 
			
		||||
, extraConfigurations ? [] }:
 | 
			
		||||
 | 
			
		||||
with import ./build-vms.nix { inherit system pkgs minimal extraConfigurations; };
 | 
			
		||||
with pkgs;
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
  jquery-ui = callPackage ./testing/jquery-ui.nix { };
 | 
			
		||||
  jquery = callPackage ./testing/jquery.nix { };
 | 
			
		||||
 | 
			
		||||
in rec {
 | 
			
		||||
 | 
			
		||||
  inherit pkgs;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  testDriver = let
 | 
			
		||||
    testDriverScript = ./test-driver/test-driver.py;
 | 
			
		||||
  in stdenv.mkDerivation {
 | 
			
		||||
    name = "nixos-test-driver";
 | 
			
		||||
 | 
			
		||||
    nativeBuildInputs = [ makeWrapper ];
 | 
			
		||||
    buildInputs = [ (python3.withPackages (p: [ p.ptpython ])) ];
 | 
			
		||||
    checkInputs = with python3Packages; [ pylint black ];
 | 
			
		||||
 | 
			
		||||
    dontUnpack = true;
 | 
			
		||||
 | 
			
		||||
    preferLocalBuild = true;
 | 
			
		||||
 | 
			
		||||
    doCheck = true;
 | 
			
		||||
    checkPhase = ''
 | 
			
		||||
      pylint --errors-only ${testDriverScript}
 | 
			
		||||
      black --check --diff ${testDriverScript}
 | 
			
		||||
    '';
 | 
			
		||||
 | 
			
		||||
    installPhase =
 | 
			
		||||
      ''
 | 
			
		||||
        mkdir -p $out/bin
 | 
			
		||||
        cp ${testDriverScript} $out/bin/nixos-test-driver
 | 
			
		||||
        chmod u+x $out/bin/nixos-test-driver
 | 
			
		||||
        # TODO: copy user script part into this file (append)
 | 
			
		||||
 | 
			
		||||
        wrapProgram $out/bin/nixos-test-driver \
 | 
			
		||||
          --prefix PATH : "${lib.makeBinPath [ qemu_test vde2 netpbm coreutils ]}" \
 | 
			
		||||
      '';
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  # Run an automated test suite in the given virtual network.
 | 
			
		||||
  # `driver' is the script that runs the network.
 | 
			
		||||
  runTests = driver:
 | 
			
		||||
    stdenv.mkDerivation {
 | 
			
		||||
      name = "vm-test-run-${driver.testName}";
 | 
			
		||||
 | 
			
		||||
      requiredSystemFeatures = [ "kvm" "nixos-test" ];
 | 
			
		||||
 | 
			
		||||
      buildInputs = [ libxslt ];
 | 
			
		||||
 | 
			
		||||
      buildCommand =
 | 
			
		||||
        ''
 | 
			
		||||
          mkdir -p $out/nix-support
 | 
			
		||||
 | 
			
		||||
          LOGFILE=$out/log.xml tests='exec(os.environ["testScript"])' ${driver}/bin/nixos-test-driver
 | 
			
		||||
 | 
			
		||||
          # Generate a pretty-printed log.
 | 
			
		||||
          xsltproc --output $out/log.html ${./test-driver/log2html.xsl} $out/log.xml
 | 
			
		||||
          ln -s ${./test-driver/logfile.css} $out/logfile.css
 | 
			
		||||
          ln -s ${./test-driver/treebits.js} $out/treebits.js
 | 
			
		||||
          ln -s ${jquery}/js/jquery.min.js $out/
 | 
			
		||||
          ln -s ${jquery}/js/jquery.js $out/
 | 
			
		||||
          ln -s ${jquery-ui}/js/jquery-ui.min.js $out/
 | 
			
		||||
          ln -s ${jquery-ui}/js/jquery-ui.js $out/
 | 
			
		||||
 | 
			
		||||
          touch $out/nix-support/hydra-build-products
 | 
			
		||||
          echo "report testlog $out log.html" >> $out/nix-support/hydra-build-products
 | 
			
		||||
 | 
			
		||||
          for i in */xchg/coverage-data; do
 | 
			
		||||
            mkdir -p $out/coverage-data
 | 
			
		||||
            mv $i $out/coverage-data/$(dirname $(dirname $i))
 | 
			
		||||
          done
 | 
			
		||||
        '';
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  makeTest =
 | 
			
		||||
    { testScript
 | 
			
		||||
    , makeCoverageReport ? false
 | 
			
		||||
    , enableOCR ? false
 | 
			
		||||
    , name ? "unnamed"
 | 
			
		||||
    , ...
 | 
			
		||||
    } @ t:
 | 
			
		||||
 | 
			
		||||
    let
 | 
			
		||||
      # A standard store path to the vm monitor is built like this:
 | 
			
		||||
      #   /tmp/nix-build-vm-test-run-$name.drv-0/vm-state-machine/monitor
 | 
			
		||||
      # The max filename length of a unix domain socket is 108 bytes.
 | 
			
		||||
      # This means $name can at most be 50 bytes long.
 | 
			
		||||
      maxTestNameLen = 50;
 | 
			
		||||
      testNameLen = builtins.stringLength name;
 | 
			
		||||
 | 
			
		||||
      testDriverName = with builtins;
 | 
			
		||||
        if testNameLen > maxTestNameLen then
 | 
			
		||||
          abort ("The name of the test '${name}' must not be longer than ${toString maxTestNameLen} " +
 | 
			
		||||
            "it's currently ${toString testNameLen} characters long.")
 | 
			
		||||
        else
 | 
			
		||||
          "nixos-test-driver-${name}";
 | 
			
		||||
 | 
			
		||||
      nodes = buildVirtualNetwork (
 | 
			
		||||
        t.nodes or (if t ? machine then { machine = t.machine; } else { }));
 | 
			
		||||
 | 
			
		||||
      testScript' =
 | 
			
		||||
        # Call the test script with the computed nodes.
 | 
			
		||||
        if lib.isFunction testScript
 | 
			
		||||
        then testScript { inherit nodes; }
 | 
			
		||||
        else testScript;
 | 
			
		||||
 | 
			
		||||
      vlans = map (m: m.config.virtualisation.vlans) (lib.attrValues nodes);
 | 
			
		||||
 | 
			
		||||
      vms = map (m: m.config.system.build.vm) (lib.attrValues nodes);
 | 
			
		||||
 | 
			
		||||
      ocrProg = tesseract4.override { enableLanguages = [ "eng" ]; };
 | 
			
		||||
 | 
			
		||||
      imagemagick_tiff = imagemagick_light.override { inherit libtiff; };
 | 
			
		||||
 | 
			
		||||
      # Generate onvenience wrappers for running the test driver
 | 
			
		||||
      # interactively with the specified network, and for starting the
 | 
			
		||||
      # VMs from the command line.
 | 
			
		||||
      driver = runCommand testDriverName
 | 
			
		||||
        { buildInputs = [ makeWrapper];
 | 
			
		||||
          testScript = testScript';
 | 
			
		||||
          preferLocalBuild = true;
 | 
			
		||||
          testName = name;
 | 
			
		||||
        }
 | 
			
		||||
        ''
 | 
			
		||||
          mkdir -p $out/bin
 | 
			
		||||
 | 
			
		||||
          echo -n "$testScript" > $out/test-script
 | 
			
		||||
          ${python3Packages.black}/bin/black --check --diff $out/test-script
 | 
			
		||||
 | 
			
		||||
          ln -s ${testDriver}/bin/nixos-test-driver $out/bin/
 | 
			
		||||
          vms=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done))
 | 
			
		||||
          wrapProgram $out/bin/nixos-test-driver \
 | 
			
		||||
            --add-flags "''${vms[*]}" \
 | 
			
		||||
            ${lib.optionalString enableOCR
 | 
			
		||||
              "--prefix PATH : '${ocrProg}/bin:${imagemagick_tiff}/bin'"} \
 | 
			
		||||
            --run "export testScript=\"\$(cat $out/test-script)\"" \
 | 
			
		||||
            --set VLANS '${toString vlans}'
 | 
			
		||||
          ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms
 | 
			
		||||
          wrapProgram $out/bin/nixos-run-vms \
 | 
			
		||||
            --add-flags "''${vms[*]}" \
 | 
			
		||||
            ${lib.optionalString enableOCR "--prefix PATH : '${ocrProg}/bin'"} \
 | 
			
		||||
            --set tests 'start_all(); join_all();' \
 | 
			
		||||
            --set VLANS '${toString vlans}' \
 | 
			
		||||
            ${lib.optionalString (builtins.length vms == 1) "--set USE_SERIAL 1"}
 | 
			
		||||
        ''; # "
 | 
			
		||||
 | 
			
		||||
      passMeta = drv: drv // lib.optionalAttrs (t ? meta) {
 | 
			
		||||
        meta = (drv.meta or {}) // t.meta;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      test = passMeta (runTests driver);
 | 
			
		||||
      report = passMeta (releaseTools.gcovReport { coverageRuns = [ test ]; });
 | 
			
		||||
 | 
			
		||||
      nodeNames = builtins.attrNames nodes;
 | 
			
		||||
      invalidNodeNames = lib.filter
 | 
			
		||||
        (node: builtins.match "^[A-z_][A-z0-9_]+$" node == null) nodeNames;
 | 
			
		||||
 | 
			
		||||
    in
 | 
			
		||||
      if lib.length invalidNodeNames > 0 then
 | 
			
		||||
        throw ''
 | 
			
		||||
          Cannot create machines out of (${lib.concatStringsSep ", " invalidNodeNames})!
 | 
			
		||||
          All machines are referenced as perl variables in the testing framework which will break the
 | 
			
		||||
          script when special characters are used.
 | 
			
		||||
 | 
			
		||||
          Please stick to alphanumeric chars and underscores as separation.
 | 
			
		||||
        ''
 | 
			
		||||
      else
 | 
			
		||||
        (if makeCoverageReport then report else test) // {
 | 
			
		||||
          inherit nodes driver test;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
  runInMachine =
 | 
			
		||||
    { drv
 | 
			
		||||
    , machine
 | 
			
		||||
    , preBuild ? ""
 | 
			
		||||
    , postBuild ? ""
 | 
			
		||||
    , ... # ???
 | 
			
		||||
    }:
 | 
			
		||||
    let
 | 
			
		||||
      vm = buildVM { }
 | 
			
		||||
        [ machine
 | 
			
		||||
          { key = "run-in-machine";
 | 
			
		||||
            networking.hostName = "client";
 | 
			
		||||
            nix.readOnlyStore = false;
 | 
			
		||||
            virtualisation.writableStore = false;
 | 
			
		||||
          }
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
      buildrunner = writeText "vm-build" ''
 | 
			
		||||
        source $1
 | 
			
		||||
 | 
			
		||||
        ${coreutils}/bin/mkdir -p $TMPDIR
 | 
			
		||||
        cd $TMPDIR
 | 
			
		||||
 | 
			
		||||
        exec $origBuilder $origArgs
 | 
			
		||||
      '';
 | 
			
		||||
 | 
			
		||||
      testScript = ''
 | 
			
		||||
        startAll;
 | 
			
		||||
        $client->waitForUnit("multi-user.target");
 | 
			
		||||
        ${preBuild}
 | 
			
		||||
        $client->succeed("env -i ${bash}/bin/bash ${buildrunner} /tmp/xchg/saved-env >&2");
 | 
			
		||||
        ${postBuild}
 | 
			
		||||
        $client->succeed("sync"); # flush all data before pulling the plug
 | 
			
		||||
      '';
 | 
			
		||||
 | 
			
		||||
      vmRunCommand = writeText "vm-run" ''
 | 
			
		||||
        xchg=vm-state-client/xchg
 | 
			
		||||
        ${coreutils}/bin/mkdir $out
 | 
			
		||||
        ${coreutils}/bin/mkdir -p $xchg
 | 
			
		||||
 | 
			
		||||
        for i in $passAsFile; do
 | 
			
		||||
          i2=''${i}Path
 | 
			
		||||
          _basename=$(${coreutils}/bin/basename ''${!i2})
 | 
			
		||||
          ${coreutils}/bin/cp ''${!i2} $xchg/$_basename
 | 
			
		||||
          eval $i2=/tmp/xchg/$_basename
 | 
			
		||||
          ${coreutils}/bin/ls -la $xchg
 | 
			
		||||
        done
 | 
			
		||||
 | 
			
		||||
        unset i i2 _basename
 | 
			
		||||
        export | ${gnugrep}/bin/grep -v '^xchg=' > $xchg/saved-env
 | 
			
		||||
        unset xchg
 | 
			
		||||
 | 
			
		||||
        export tests='${testScript}'
 | 
			
		||||
        ${testDriver}/bin/nixos-test-driver ${vm.config.system.build.vm}/bin/run-*-vm
 | 
			
		||||
      ''; # */
 | 
			
		||||
 | 
			
		||||
    in
 | 
			
		||||
      lib.overrideDerivation drv (attrs: {
 | 
			
		||||
        requiredSystemFeatures = [ "kvm" ];
 | 
			
		||||
        builder = "${bash}/bin/sh";
 | 
			
		||||
        args = ["-e" vmRunCommand];
 | 
			
		||||
        origArgs = attrs.args;
 | 
			
		||||
        origBuilder = attrs.builder;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  runInMachineWithX = { require ? [], ... } @ args:
 | 
			
		||||
    let
 | 
			
		||||
      client =
 | 
			
		||||
        { ... }:
 | 
			
		||||
        {
 | 
			
		||||
          inherit require;
 | 
			
		||||
          virtualisation.memorySize = 1024;
 | 
			
		||||
          services.xserver.enable = true;
 | 
			
		||||
          services.xserver.displayManager.slim.enable = false;
 | 
			
		||||
          services.xserver.displayManager.auto.enable = true;
 | 
			
		||||
          services.xserver.windowManager.default = "icewm";
 | 
			
		||||
          services.xserver.windowManager.icewm.enable = true;
 | 
			
		||||
          services.xserver.desktopManager.default = "none";
 | 
			
		||||
        };
 | 
			
		||||
    in
 | 
			
		||||
      runInMachine ({
 | 
			
		||||
        machine = client;
 | 
			
		||||
        preBuild =
 | 
			
		||||
          ''
 | 
			
		||||
            $client->waitForX;
 | 
			
		||||
          '';
 | 
			
		||||
      } // args);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  simpleTest = as: (makeTest as).test;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								nixos/modules/hardware/brillo.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								nixos/modules/hardware/brillo.nix
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
{ config, lib, pkgs, ... }:
 | 
			
		||||
 | 
			
		||||
with lib;
 | 
			
		||||
let
 | 
			
		||||
  cfg = config.hardware.brillo;
 | 
			
		||||
in
 | 
			
		||||
{
 | 
			
		||||
  options = {
 | 
			
		||||
    hardware.brillo = {
 | 
			
		||||
      enable = mkEnableOption ''
 | 
			
		||||
        Enable brillo in userspace.
 | 
			
		||||
        This will allow brightness control from users in the video group.
 | 
			
		||||
      '';
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  config = mkIf cfg.enable {
 | 
			
		||||
    services.udev.packages = [ pkgs.brillo ];
 | 
			
		||||
    environment.systemPackages = [ pkgs.brillo ];
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
@ -1,327 +0,0 @@
 | 
			
		||||
#! @shell@ -e
 | 
			
		||||
 | 
			
		||||
# FIXME: rewrite this in a more suitable language.
 | 
			
		||||
 | 
			
		||||
usage () {
 | 
			
		||||
    exec man nixos-option
 | 
			
		||||
    exit 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#####################
 | 
			
		||||
# Process Arguments #
 | 
			
		||||
#####################
 | 
			
		||||
 | 
			
		||||
xml=false
 | 
			
		||||
verbose=false
 | 
			
		||||
nixPath=""
 | 
			
		||||
 | 
			
		||||
option=""
 | 
			
		||||
exit_code=0
 | 
			
		||||
 | 
			
		||||
argfun=""
 | 
			
		||||
for arg; do
 | 
			
		||||
  if test -z "$argfun"; then
 | 
			
		||||
    case $arg in
 | 
			
		||||
      -*)
 | 
			
		||||
        sarg="$arg"
 | 
			
		||||
        longarg=""
 | 
			
		||||
        while test "$sarg" != "-"; do
 | 
			
		||||
          case $sarg in
 | 
			
		||||
            --*) longarg=$arg; sarg="--";;
 | 
			
		||||
            -I) argfun="include_nixpath";;
 | 
			
		||||
            -*) usage;;
 | 
			
		||||
          esac
 | 
			
		||||
          # remove the first letter option
 | 
			
		||||
          sarg="-${sarg#??}"
 | 
			
		||||
        done
 | 
			
		||||
        ;;
 | 
			
		||||
      *) longarg=$arg;;
 | 
			
		||||
    esac
 | 
			
		||||
    for larg in $longarg; do
 | 
			
		||||
      case $larg in
 | 
			
		||||
        --xml) xml=true;;
 | 
			
		||||
        --verbose) verbose=true;;
 | 
			
		||||
        --help) usage;;
 | 
			
		||||
        -*) usage;;
 | 
			
		||||
        *) if test -z "$option"; then
 | 
			
		||||
             option="$larg"
 | 
			
		||||
           else
 | 
			
		||||
             usage
 | 
			
		||||
           fi;;
 | 
			
		||||
      esac
 | 
			
		||||
    done
 | 
			
		||||
  else
 | 
			
		||||
    case $argfun in
 | 
			
		||||
      set_*)
 | 
			
		||||
        var=$(echo $argfun | sed 's,^set_,,')
 | 
			
		||||
        eval $var=$arg
 | 
			
		||||
        ;;
 | 
			
		||||
      include_nixpath)
 | 
			
		||||
        nixPath="-I $arg $nixPath"
 | 
			
		||||
        ;;
 | 
			
		||||
    esac
 | 
			
		||||
    argfun=""
 | 
			
		||||
  fi
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
if $verbose; then
 | 
			
		||||
  set -x
 | 
			
		||||
else
 | 
			
		||||
  set +x
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
#############################
 | 
			
		||||
# Process the configuration #
 | 
			
		||||
#############################
 | 
			
		||||
 | 
			
		||||
evalNix(){
 | 
			
		||||
  # disable `-e` flag, it's possible that the evaluation of `nix-instantiate` fails (e.g. due to broken pkgs)
 | 
			
		||||
  set +e
 | 
			
		||||
  result=$(nix-instantiate ${nixPath:+$nixPath} - --eval-only "$@" 2>&1)
 | 
			
		||||
  exit_code=$?
 | 
			
		||||
  set -e
 | 
			
		||||
 | 
			
		||||
  if test $exit_code -eq 0; then
 | 
			
		||||
      sed '/^warning: Nix search path/d' <<EOF
 | 
			
		||||
$result
 | 
			
		||||
EOF
 | 
			
		||||
      return 0;
 | 
			
		||||
  else
 | 
			
		||||
      sed -n '
 | 
			
		||||
  /^error/ { s/, at (string):[0-9]*:[0-9]*//; p; };
 | 
			
		||||
  /^warning: Nix search path/ { p; };
 | 
			
		||||
' >&2 <<EOF
 | 
			
		||||
$result
 | 
			
		||||
EOF
 | 
			
		||||
    exit_code=1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
header="let
 | 
			
		||||
  nixos = import <nixpkgs/nixos> {};
 | 
			
		||||
  nixpkgs = import <nixpkgs> {};
 | 
			
		||||
in with nixpkgs.lib;
 | 
			
		||||
"
 | 
			
		||||
 | 
			
		||||
# This function is used for converting the option definition path given by
 | 
			
		||||
# the user into accessors for reaching the definition and the declaration
 | 
			
		||||
# corresponding to this option.
 | 
			
		||||
generateAccessors(){
 | 
			
		||||
  if result=$(evalNix --strict --show-trace <<EOF
 | 
			
		||||
$header
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
  path = "${option:+$option}";
 | 
			
		||||
  pathList = splitString "." path;
 | 
			
		||||
 | 
			
		||||
  walkOptions = attrsNames: result:
 | 
			
		||||
    if attrsNames == [] then
 | 
			
		||||
      result
 | 
			
		||||
    else
 | 
			
		||||
      let name = head attrsNames; rest = tail attrsNames; in
 | 
			
		||||
      if isOption result.options then
 | 
			
		||||
        walkOptions rest {
 | 
			
		||||
          options = result.options.type.getSubOptions "";
 | 
			
		||||
          opt = ''(\${result.opt}.type.getSubOptions "")'';
 | 
			
		||||
          cfg = ''\${result.cfg}."\${name}"'';
 | 
			
		||||
        }
 | 
			
		||||
      else
 | 
			
		||||
        walkOptions rest {
 | 
			
		||||
          options = result.options.\${name};
 | 
			
		||||
          opt = ''\${result.opt}."\${name}"'';
 | 
			
		||||
          cfg = ''\${result.cfg}."\${name}"'';
 | 
			
		||||
        }
 | 
			
		||||
    ;
 | 
			
		||||
 | 
			
		||||
  walkResult = (if path == "" then x: x else walkOptions pathList) {
 | 
			
		||||
    options = nixos.options;
 | 
			
		||||
    opt = ''nixos.options'';
 | 
			
		||||
    cfg = ''nixos.config'';
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
in
 | 
			
		||||
  ''let option = \${walkResult.opt}; config = \${walkResult.cfg}; in''
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
  then
 | 
			
		||||
      echo $result
 | 
			
		||||
  else
 | 
			
		||||
      # In case of error we want to ignore the error message roduced by the
 | 
			
		||||
      # script above, as it is iterating over each attribute, which does not
 | 
			
		||||
      # produce a nice error message.  The following code is a fallback
 | 
			
		||||
      # solution which is cause a nicer error message in the next
 | 
			
		||||
      # evaluation.
 | 
			
		||||
      echo "\"let option = nixos.options${option:+.$option}; config = nixos.config${option:+.$option}; in\""
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
header="$header
 | 
			
		||||
$(eval echo $(generateAccessors))
 | 
			
		||||
"
 | 
			
		||||
 | 
			
		||||
evalAttr(){
 | 
			
		||||
  local prefix="$1"
 | 
			
		||||
  local strict="$2"
 | 
			
		||||
  local suffix="$3"
 | 
			
		||||
 | 
			
		||||
  # If strict is set, then set it to "true".
 | 
			
		||||
  test -n "$strict" && strict=true
 | 
			
		||||
 | 
			
		||||
  evalNix ${strict:+--strict} <<EOF
 | 
			
		||||
$header
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
  value = $prefix${suffix:+.$suffix};
 | 
			
		||||
  strict = ${strict:-false};
 | 
			
		||||
  cleanOutput = x: with nixpkgs.lib;
 | 
			
		||||
    if isDerivation x then x.outPath
 | 
			
		||||
    else if isFunction x then "<CODE>"
 | 
			
		||||
    else if strict then
 | 
			
		||||
      if isAttrs x then mapAttrs (n: cleanOutput) x
 | 
			
		||||
      else if isList x then map cleanOutput x
 | 
			
		||||
      else x
 | 
			
		||||
    else x;
 | 
			
		||||
in
 | 
			
		||||
  cleanOutput value
 | 
			
		||||
EOF
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
evalOpt(){
 | 
			
		||||
  evalAttr "option" "" "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
evalCfg(){
 | 
			
		||||
  local strict="$1"
 | 
			
		||||
  evalAttr "config" "$strict"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
findSources(){
 | 
			
		||||
  local suffix=$1
 | 
			
		||||
  evalNix --strict <<EOF
 | 
			
		||||
$header
 | 
			
		||||
 | 
			
		||||
option.$suffix
 | 
			
		||||
EOF
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Given a result from nix-instantiate, recover the list of attributes it
 | 
			
		||||
# contains.
 | 
			
		||||
attrNames() {
 | 
			
		||||
  local attributeset=$1
 | 
			
		||||
  # sed is used to replace un-printable subset by 0s, and to remove most of
 | 
			
		||||
  # the inner-attribute set, which reduce the likelyhood to encounter badly
 | 
			
		||||
  # pre-processed input.
 | 
			
		||||
  echo "builtins.attrNames $attributeset" | \
 | 
			
		||||
    sed 's,<[A-Z]*>,0,g; :inner; s/{[^\{\}]*};/0;/g; t inner;' | \
 | 
			
		||||
    evalNix --strict
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# map a simple list which contains strings or paths.
 | 
			
		||||
nixMap() {
 | 
			
		||||
  local fun="$1"
 | 
			
		||||
  local list="$2"
 | 
			
		||||
  local elem
 | 
			
		||||
  for elem in $list; do
 | 
			
		||||
    test $elem = '[' -o $elem = ']' && continue;
 | 
			
		||||
    $fun $elem
 | 
			
		||||
  done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# This duplicates the work made below, but it is useful for processing
 | 
			
		||||
# the output of nixos-option with other tools such as nixos-gui.
 | 
			
		||||
if $xml; then
 | 
			
		||||
  evalNix --xml --no-location <<EOF
 | 
			
		||||
$header
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
  sources = builtins.map (f: f.source);
 | 
			
		||||
  opt = option;
 | 
			
		||||
  cfg = config;
 | 
			
		||||
in
 | 
			
		||||
 | 
			
		||||
with nixpkgs.lib;
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
  optStrict = v:
 | 
			
		||||
    let
 | 
			
		||||
      traverse = x :
 | 
			
		||||
        if isAttrs x then
 | 
			
		||||
          if x ? outPath then true
 | 
			
		||||
          else all id (mapAttrsFlatten (n: traverseNoAttrs) x)
 | 
			
		||||
        else traverseNoAttrs x;
 | 
			
		||||
      traverseNoAttrs = x:
 | 
			
		||||
        # do not continue in attribute sets
 | 
			
		||||
        if isAttrs x then true
 | 
			
		||||
        else if isList x then all id (map traverse x)
 | 
			
		||||
        else true;
 | 
			
		||||
    in assert traverse v; v;
 | 
			
		||||
in
 | 
			
		||||
 | 
			
		||||
if isOption opt then
 | 
			
		||||
  optStrict ({}
 | 
			
		||||
  // optionalAttrs (opt ? default) { inherit (opt) default; }
 | 
			
		||||
  // optionalAttrs (opt ? example) { inherit (opt) example; }
 | 
			
		||||
  // optionalAttrs (opt ? description) { inherit (opt) description; }
 | 
			
		||||
  // optionalAttrs (opt ? type) { typename = opt.type.description; }
 | 
			
		||||
  // optionalAttrs (opt ? options) { inherit (opt) options; }
 | 
			
		||||
  // {
 | 
			
		||||
    # to disambiguate the xml output.
 | 
			
		||||
    _isOption = true;
 | 
			
		||||
    declarations = sources opt.declarations;
 | 
			
		||||
    definitions = sources opt.definitions;
 | 
			
		||||
    value = cfg;
 | 
			
		||||
  })
 | 
			
		||||
else
 | 
			
		||||
  opt
 | 
			
		||||
EOF
 | 
			
		||||
  exit $?
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if test "$(evalOpt "_type" 2> /dev/null)" = '"option"'; then
 | 
			
		||||
  echo "Value:"
 | 
			
		||||
  evalCfg 1
 | 
			
		||||
 | 
			
		||||
  echo
 | 
			
		||||
 | 
			
		||||
  echo "Default:"
 | 
			
		||||
  if default=$(evalOpt "default" - 2> /dev/null); then
 | 
			
		||||
    echo "$default"
 | 
			
		||||
  else
 | 
			
		||||
    echo "<None>"
 | 
			
		||||
  fi
 | 
			
		||||
  echo
 | 
			
		||||
  if example=$(evalOpt "example" - 2> /dev/null); then
 | 
			
		||||
    echo "Example:"
 | 
			
		||||
    echo "$example"
 | 
			
		||||
    echo
 | 
			
		||||
  fi
 | 
			
		||||
  echo "Description:"
 | 
			
		||||
  echo
 | 
			
		||||
  echo $(evalOpt "description")
 | 
			
		||||
 | 
			
		||||
  echo $desc;
 | 
			
		||||
 | 
			
		||||
  printPath () { echo "  $1"; }
 | 
			
		||||
 | 
			
		||||
  echo "Declared by:"
 | 
			
		||||
  nixMap printPath "$(findSources "declarations")"
 | 
			
		||||
  echo
 | 
			
		||||
  echo "Defined by:"
 | 
			
		||||
  nixMap printPath "$(findSources "files")"
 | 
			
		||||
  echo
 | 
			
		||||
 | 
			
		||||
else
 | 
			
		||||
  # echo 1>&2 "Warning: This value is not an option."
 | 
			
		||||
 | 
			
		||||
  result=$(evalCfg "")
 | 
			
		||||
  if [ ! -z "$result" ]; then
 | 
			
		||||
    names=$(attrNames "$result" 2> /dev/null)
 | 
			
		||||
    echo 1>&2 "This attribute set contains:"
 | 
			
		||||
    escapeQuotes () { eval echo "$1"; }
 | 
			
		||||
    nixMap escapeQuotes "$names"
 | 
			
		||||
  else
 | 
			
		||||
    echo 1>&2 "An error occurred while looking for attribute names. Are you sure that '$option' exists?"
 | 
			
		||||
  fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
exit $exit_code
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
cmake_minimum_required (VERSION 2.6)
 | 
			
		||||
project (nixos-option)
 | 
			
		||||
 | 
			
		||||
add_executable(nixos-option nixos-option.cc libnix-copy-paste.cc)
 | 
			
		||||
target_link_libraries(nixos-option PRIVATE -lnixmain -lnixexpr -lnixstore -lnixutil)
 | 
			
		||||
target_compile_features(nixos-option PRIVATE cxx_std_17)
 | 
			
		||||
 | 
			
		||||
install (TARGETS nixos-option DESTINATION bin)
 | 
			
		||||
							
								
								
									
										11
									
								
								nixos/modules/installer/tools/nixos-option/default.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								nixos/modules/installer/tools/nixos-option/default.nix
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
{lib, stdenv, boost, cmake, pkgconfig, nix, ... }:
 | 
			
		||||
stdenv.mkDerivation rec {
 | 
			
		||||
  name = "nixos-option";
 | 
			
		||||
  src = ./.;
 | 
			
		||||
  nativeBuildInputs = [ cmake pkgconfig ];
 | 
			
		||||
  buildInputs = [ boost nix ];
 | 
			
		||||
  meta = {
 | 
			
		||||
    license = stdenv.lib.licenses.lgpl2Plus;
 | 
			
		||||
    maintainers = with lib.maintainers; [ chkno ];
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,83 @@
 | 
			
		||||
// These are useful methods inside the nix library that ought to be exported.
 | 
			
		||||
// Since they are not, copy/paste them here.
 | 
			
		||||
// TODO: Delete these and use the ones in the library as they become available.
 | 
			
		||||
 | 
			
		||||
#include <nix/config.h> // for nix/globals.hh's reference to SYSTEM
 | 
			
		||||
 | 
			
		||||
#include "libnix-copy-paste.hh"
 | 
			
		||||
#include <boost/format/alt_sstream.hpp>           // for basic_altstringbuf...
 | 
			
		||||
#include <boost/format/alt_sstream_impl.hpp>      // for basic_altstringbuf...
 | 
			
		||||
#include <boost/format/format_class.hpp>          // for basic_format
 | 
			
		||||
#include <boost/format/format_fwd.hpp>            // for format
 | 
			
		||||
#include <boost/format/format_implementation.hpp> // for basic_format::basi...
 | 
			
		||||
#include <boost/optional/optional.hpp>            // for get_pointer
 | 
			
		||||
#include <iostream>                               // for operator<<, basic_...
 | 
			
		||||
#include <nix/types.hh>                           // for Strings, Error
 | 
			
		||||
#include <string>                                 // for string, basic_string
 | 
			
		||||
 | 
			
		||||
using boost::format;
 | 
			
		||||
using nix::Error;
 | 
			
		||||
using nix::Strings;
 | 
			
		||||
using std::string;
 | 
			
		||||
 | 
			
		||||
// From nix/src/libexpr/attr-path.cc
 | 
			
		||||
Strings parseAttrPath(const string & s)
 | 
			
		||||
{
 | 
			
		||||
    Strings res;
 | 
			
		||||
    string cur;
 | 
			
		||||
    string::const_iterator i = s.begin();
 | 
			
		||||
    while (i != s.end()) {
 | 
			
		||||
        if (*i == '.') {
 | 
			
		||||
            res.push_back(cur);
 | 
			
		||||
            cur.clear();
 | 
			
		||||
        } else if (*i == '"') {
 | 
			
		||||
            ++i;
 | 
			
		||||
            while (1) {
 | 
			
		||||
                if (i == s.end())
 | 
			
		||||
                    throw Error(format("missing closing quote in selection path '%1%'") % s);
 | 
			
		||||
                if (*i == '"')
 | 
			
		||||
                    break;
 | 
			
		||||
                cur.push_back(*i++);
 | 
			
		||||
            }
 | 
			
		||||
        } else
 | 
			
		||||
            cur.push_back(*i);
 | 
			
		||||
        ++i;
 | 
			
		||||
    }
 | 
			
		||||
    if (!cur.empty())
 | 
			
		||||
        res.push_back(cur);
 | 
			
		||||
    return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// From nix/src/nix/repl.cc
 | 
			
		||||
bool isVarName(const string & s)
 | 
			
		||||
{
 | 
			
		||||
    if (s.size() == 0)
 | 
			
		||||
        return false;
 | 
			
		||||
    char c = s[0];
 | 
			
		||||
    if ((c >= '0' && c <= '9') || c == '-' || c == '\'')
 | 
			
		||||
        return false;
 | 
			
		||||
    for (auto & i : s)
 | 
			
		||||
        if (!((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_' || i == '-' ||
 | 
			
		||||
              i == '\''))
 | 
			
		||||
            return false;
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// From nix/src/nix/repl.cc
 | 
			
		||||
std::ostream & printStringValue(std::ostream & str, const char * string)
 | 
			
		||||
{
 | 
			
		||||
    str << "\"";
 | 
			
		||||
    for (const char * i = string; *i; i++)
 | 
			
		||||
        if (*i == '\"' || *i == '\\')
 | 
			
		||||
            str << "\\" << *i;
 | 
			
		||||
        else if (*i == '\n')
 | 
			
		||||
            str << "\\n";
 | 
			
		||||
        else if (*i == '\r')
 | 
			
		||||
            str << "\\r";
 | 
			
		||||
        else if (*i == '\t')
 | 
			
		||||
            str << "\\t";
 | 
			
		||||
        else
 | 
			
		||||
            str << *i;
 | 
			
		||||
    str << "\"";
 | 
			
		||||
    return str;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <nix/types.hh>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
nix::Strings parseAttrPath(const std::string & s);
 | 
			
		||||
bool isVarName(const std::string & s);
 | 
			
		||||
std::ostream & printStringValue(std::ostream & str, const char * string);
 | 
			
		||||
							
								
								
									
										618
									
								
								nixos/modules/installer/tools/nixos-option/nixos-option.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										618
									
								
								nixos/modules/installer/tools/nixos-option/nixos-option.cc
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,618 @@
 | 
			
		||||
#include <nix/config.h> // for nix/globals.hh's reference to SYSTEM
 | 
			
		||||
 | 
			
		||||
#include <exception>               // for exception_ptr, current_exception
 | 
			
		||||
#include <functional>              // for function
 | 
			
		||||
#include <iostream>                // for operator<<, basic_ostream, ostrin...
 | 
			
		||||
#include <iterator>                // for next
 | 
			
		||||
#include <list>                    // for _List_iterator
 | 
			
		||||
#include <memory>                  // for allocator, unique_ptr, make_unique
 | 
			
		||||
#include <new>                     // for operator new
 | 
			
		||||
#include <nix/args.hh>             // for argvToStrings, UsageError
 | 
			
		||||
#include <nix/attr-path.hh>        // for findAlongAttrPath
 | 
			
		||||
#include <nix/attr-set.hh>         // for Attr, Bindings, Bindings::iterator
 | 
			
		||||
#include <nix/common-eval-args.hh> // for MixEvalArgs
 | 
			
		||||
#include <nix/eval-inline.hh>      // for EvalState::forceValue
 | 
			
		||||
#include <nix/eval.hh>             // for EvalState, initGC, operator<<
 | 
			
		||||
#include <nix/globals.hh>          // for initPlugins, Settings, settings
 | 
			
		||||
#include <nix/nixexpr.hh>          // for Pos
 | 
			
		||||
#include <nix/shared.hh>           // for getArg, LegacyArgs, printVersion
 | 
			
		||||
#include <nix/store-api.hh>        // for openStore
 | 
			
		||||
#include <nix/symbol-table.hh>     // for Symbol, SymbolTable
 | 
			
		||||
#include <nix/types.hh>            // for Error, Path, Strings, PathSet
 | 
			
		||||
#include <nix/util.hh>             // for absPath, baseNameOf
 | 
			
		||||
#include <nix/value.hh>            // for Value, Value::(anonymous), Value:...
 | 
			
		||||
#include <string>                  // for string, operator+, operator==
 | 
			
		||||
#include <utility>                 // for move
 | 
			
		||||
#include <variant>                 // for get, holds_alternative, variant
 | 
			
		||||
#include <vector>                  // for vector<>::iterator, vector
 | 
			
		||||
 | 
			
		||||
#include "libnix-copy-paste.hh"
 | 
			
		||||
 | 
			
		||||
using nix::absPath;
 | 
			
		||||
using nix::Bindings;
 | 
			
		||||
using nix::Error;
 | 
			
		||||
using nix::EvalError;
 | 
			
		||||
using nix::EvalState;
 | 
			
		||||
using nix::Path;
 | 
			
		||||
using nix::PathSet;
 | 
			
		||||
using nix::Strings;
 | 
			
		||||
using nix::Symbol;
 | 
			
		||||
using nix::tAttrs;
 | 
			
		||||
using nix::ThrownError;
 | 
			
		||||
using nix::tLambda;
 | 
			
		||||
using nix::tString;
 | 
			
		||||
using nix::UsageError;
 | 
			
		||||
using nix::Value;
 | 
			
		||||
 | 
			
		||||
// An ostream wrapper to handle nested indentation
 | 
			
		||||
class Out
 | 
			
		||||
{
 | 
			
		||||
  public:
 | 
			
		||||
    class Separator
 | 
			
		||||
    {};
 | 
			
		||||
    const static Separator sep;
 | 
			
		||||
    enum LinePolicy
 | 
			
		||||
    {
 | 
			
		||||
        ONE_LINE,
 | 
			
		||||
        MULTI_LINE
 | 
			
		||||
    };
 | 
			
		||||
    explicit Out(std::ostream & ostream) : ostream(ostream), policy(ONE_LINE), writeSinceSep(true) {}
 | 
			
		||||
    Out(Out & o, const std::string & start, const std::string & end, LinePolicy policy);
 | 
			
		||||
    Out(Out & o, const std::string & start, const std::string & end, int count)
 | 
			
		||||
        : Out(o, start, end, count < 2 ? ONE_LINE : MULTI_LINE)
 | 
			
		||||
    {}
 | 
			
		||||
    Out(const Out &) = delete;
 | 
			
		||||
    Out(Out &&) = default;
 | 
			
		||||
    Out & operator=(const Out &) = delete;
 | 
			
		||||
    Out & operator=(Out &&) = delete;
 | 
			
		||||
    ~Out() { ostream << end; }
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    std::ostream & ostream;
 | 
			
		||||
    std::string indentation;
 | 
			
		||||
    std::string end;
 | 
			
		||||
    LinePolicy policy;
 | 
			
		||||
    bool writeSinceSep;
 | 
			
		||||
    template <typename T> friend Out & operator<<(Out & o, T thing);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename T> Out & operator<<(Out & o, T thing)
 | 
			
		||||
{
 | 
			
		||||
    if (!o.writeSinceSep && o.policy == Out::MULTI_LINE) {
 | 
			
		||||
        o.ostream << o.indentation;
 | 
			
		||||
    }
 | 
			
		||||
    o.writeSinceSep = true;
 | 
			
		||||
    o.ostream << thing;
 | 
			
		||||
    return o;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <> Out & operator<<<Out::Separator>(Out & o, Out::Separator /* thing */)
 | 
			
		||||
{
 | 
			
		||||
    o.ostream << (o.policy == Out::ONE_LINE ? " " : "\n");
 | 
			
		||||
    o.writeSinceSep = false;
 | 
			
		||||
    return o;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Out::Out(Out & o, const std::string & start, const std::string & end, LinePolicy policy)
 | 
			
		||||
    : ostream(o.ostream), indentation(policy == ONE_LINE ? o.indentation : o.indentation + "  "),
 | 
			
		||||
      end(policy == ONE_LINE ? end : o.indentation + end), policy(policy), writeSinceSep(true)
 | 
			
		||||
{
 | 
			
		||||
    o << start;
 | 
			
		||||
    *this << Out::sep;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stuff needed for evaluation
 | 
			
		||||
struct Context
 | 
			
		||||
{
 | 
			
		||||
    Context(EvalState & state, Bindings & autoArgs, Value optionsRoot, Value configRoot)
 | 
			
		||||
        : state(state), autoArgs(autoArgs), optionsRoot(optionsRoot), configRoot(configRoot),
 | 
			
		||||
          underscoreType(state.symbols.create("_type"))
 | 
			
		||||
    {}
 | 
			
		||||
    EvalState & state;
 | 
			
		||||
    Bindings & autoArgs;
 | 
			
		||||
    Value optionsRoot;
 | 
			
		||||
    Value configRoot;
 | 
			
		||||
    Symbol underscoreType;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Value evaluateValue(Context & ctx, Value & v)
 | 
			
		||||
{
 | 
			
		||||
    ctx.state.forceValue(v);
 | 
			
		||||
    if (ctx.autoArgs.empty()) {
 | 
			
		||||
        return v;
 | 
			
		||||
    }
 | 
			
		||||
    Value called{};
 | 
			
		||||
    ctx.state.autoCallFunction(ctx.autoArgs, v, called);
 | 
			
		||||
    return called;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool isOption(Context & ctx, const Value & v)
 | 
			
		||||
{
 | 
			
		||||
    if (v.type != tAttrs) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    const auto & atualType = v.attrs->find(ctx.underscoreType);
 | 
			
		||||
    if (atualType == v.attrs->end()) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    try {
 | 
			
		||||
        Value evaluatedType = evaluateValue(ctx, *atualType->value);
 | 
			
		||||
        if (evaluatedType.type != tString) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        return static_cast<std::string>(evaluatedType.string.s) == "option";
 | 
			
		||||
    } catch (Error &) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add quotes to a component of a path.
 | 
			
		||||
// These are needed for paths like:
 | 
			
		||||
//    fileSystems."/".fsType
 | 
			
		||||
//    systemd.units."dbus.service".text
 | 
			
		||||
std::string quoteAttribute(const std::string & attribute)
 | 
			
		||||
{
 | 
			
		||||
    if (isVarName(attribute)) {
 | 
			
		||||
        return attribute;
 | 
			
		||||
    }
 | 
			
		||||
    std::ostringstream buf;
 | 
			
		||||
    printStringValue(buf, attribute.c_str());
 | 
			
		||||
    return buf.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const std::string appendPath(const std::string & prefix, const std::string & suffix)
 | 
			
		||||
{
 | 
			
		||||
    if (prefix.empty()) {
 | 
			
		||||
        return quoteAttribute(suffix);
 | 
			
		||||
    }
 | 
			
		||||
    return prefix + "." + quoteAttribute(suffix);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool forbiddenRecursionName(std::string name) { return (!name.empty() && name[0] == '_') || name == "haskellPackages"; }
 | 
			
		||||
 | 
			
		||||
void recurse(const std::function<bool(const std::string & path, std::variant<Value, std::exception_ptr>)> & f,
 | 
			
		||||
             Context & ctx, Value v, const std::string & path)
 | 
			
		||||
{
 | 
			
		||||
    std::variant<Value, std::exception_ptr> evaluated;
 | 
			
		||||
    try {
 | 
			
		||||
        evaluated = evaluateValue(ctx, v);
 | 
			
		||||
    } catch (Error &) {
 | 
			
		||||
        evaluated = std::current_exception();
 | 
			
		||||
    }
 | 
			
		||||
    if (!f(path, evaluated)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (std::holds_alternative<std::exception_ptr>(evaluated)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    const Value & evaluated_value = std::get<Value>(evaluated);
 | 
			
		||||
    if (evaluated_value.type != tAttrs) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    for (const auto & child : evaluated_value.attrs->lexicographicOrder()) {
 | 
			
		||||
        if (forbiddenRecursionName(child->name)) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        recurse(f, ctx, *child->value, appendPath(path, child->name));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Calls f on all the option names
 | 
			
		||||
void mapOptions(const std::function<void(const std::string & path)> & f, Context & ctx, Value root)
 | 
			
		||||
{
 | 
			
		||||
    recurse(
 | 
			
		||||
        [f, &ctx](const std::string & path, std::variant<Value, std::exception_ptr> v) {
 | 
			
		||||
            bool isOpt = std::holds_alternative<std::exception_ptr>(v) || isOption(ctx, std::get<Value>(v));
 | 
			
		||||
            if (isOpt) {
 | 
			
		||||
                f(path);
 | 
			
		||||
            }
 | 
			
		||||
            return !isOpt;
 | 
			
		||||
        },
 | 
			
		||||
        ctx, root, "");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Calls f on all the config values inside one option.
 | 
			
		||||
// Simple options have one config value inside, like sound.enable = true.
 | 
			
		||||
// Compound options have multiple config values.  For example, the option
 | 
			
		||||
// "users.users" has about 1000 config values inside it:
 | 
			
		||||
//   users.users.avahi.createHome = false;
 | 
			
		||||
//   users.users.avahi.cryptHomeLuks = null;
 | 
			
		||||
//   users.users.avahi.description = "`avahi-daemon' privilege separation user";
 | 
			
		||||
//   ...
 | 
			
		||||
//   users.users.avahi.openssh.authorizedKeys.keyFiles = [ ];
 | 
			
		||||
//   users.users.avahi.openssh.authorizedKeys.keys = [ ];
 | 
			
		||||
//   ...
 | 
			
		||||
//   users.users.avahi.uid = 10;
 | 
			
		||||
//   users.users.avahi.useDefaultShell = false;
 | 
			
		||||
//   users.users.cups.createHome = false;
 | 
			
		||||
//   ...
 | 
			
		||||
//   users.users.cups.useDefaultShell = false;
 | 
			
		||||
//   users.users.gdm = ... ... ...
 | 
			
		||||
//   users.users.messagebus = ... .. ...
 | 
			
		||||
//   users.users.nixbld1 = ... .. ...
 | 
			
		||||
//   ...
 | 
			
		||||
//   users.users.systemd-timesync = ... .. ...
 | 
			
		||||
void mapConfigValuesInOption(
 | 
			
		||||
    const std::function<void(const std::string & path, std::variant<Value, std::exception_ptr> v)> & f,
 | 
			
		||||
    const std::string & path, Context & ctx)
 | 
			
		||||
{
 | 
			
		||||
    Value * option;
 | 
			
		||||
    try {
 | 
			
		||||
        option = findAlongAttrPath(ctx.state, path, ctx.autoArgs, ctx.configRoot);
 | 
			
		||||
    } catch (Error &) {
 | 
			
		||||
        f(path, std::current_exception());
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    recurse(
 | 
			
		||||
        [f, ctx](const std::string & path, std::variant<Value, std::exception_ptr> v) {
 | 
			
		||||
            bool leaf = std::holds_alternative<std::exception_ptr>(v) || std::get<Value>(v).type != tAttrs ||
 | 
			
		||||
                        ctx.state.isDerivation(std::get<Value>(v));
 | 
			
		||||
            if (!leaf) {
 | 
			
		||||
                return true; // Keep digging
 | 
			
		||||
            }
 | 
			
		||||
            f(path, v);
 | 
			
		||||
            return false;
 | 
			
		||||
        },
 | 
			
		||||
        ctx, *option, path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string describeError(const Error & e) { return "«error: " + e.msg() + "»"; }
 | 
			
		||||
 | 
			
		||||
void describeDerivation(Context & ctx, Out & out, Value v)
 | 
			
		||||
{
 | 
			
		||||
    // Copy-pasted from nix/src/nix/repl.cc  :(
 | 
			
		||||
    Bindings::iterator i = v.attrs->find(ctx.state.sDrvPath);
 | 
			
		||||
    PathSet pathset;
 | 
			
		||||
    try {
 | 
			
		||||
        Path drvPath = i != v.attrs->end() ? ctx.state.coerceToPath(*i->pos, *i->value, pathset) : "???";
 | 
			
		||||
        out << "«derivation " << drvPath << "»";
 | 
			
		||||
    } catch (Error & e) {
 | 
			
		||||
        out << describeError(e);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Value parseAndEval(EvalState & state, const std::string & expression, const std::string & path)
 | 
			
		||||
{
 | 
			
		||||
    Value v{};
 | 
			
		||||
    state.eval(state.parseExprFromString(expression, absPath(path)), v);
 | 
			
		||||
    return v;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void printValue(Context & ctx, Out & out, std::variant<Value, std::exception_ptr> maybeValue, const std::string & path);
 | 
			
		||||
 | 
			
		||||
void printList(Context & ctx, Out & out, Value & v)
 | 
			
		||||
{
 | 
			
		||||
    Out listOut(out, "[", "]", v.listSize());
 | 
			
		||||
    for (unsigned int n = 0; n < v.listSize(); ++n) {
 | 
			
		||||
        printValue(ctx, listOut, *v.listElems()[n], "");
 | 
			
		||||
        listOut << Out::sep;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void printAttrs(Context & ctx, Out & out, Value & v, const std::string & path)
 | 
			
		||||
{
 | 
			
		||||
    Out attrsOut(out, "{", "}", v.attrs->size());
 | 
			
		||||
    for (const auto & a : v.attrs->lexicographicOrder()) {
 | 
			
		||||
        std::string name = a->name;
 | 
			
		||||
        attrsOut << name << " = ";
 | 
			
		||||
        printValue(ctx, attrsOut, *a->value, appendPath(path, name));
 | 
			
		||||
        attrsOut << ";" << Out::sep;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void multiLineStringEscape(Out & out, const std::string & s)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    for (i = 1; i < s.size(); i++) {
 | 
			
		||||
        if (s[i - 1] == '$' && s[i] == '{') {
 | 
			
		||||
            out << "''${";
 | 
			
		||||
            i++;
 | 
			
		||||
        } else if (s[i - 1] == '\'' && s[i] == '\'') {
 | 
			
		||||
            out << "'''";
 | 
			
		||||
            i++;
 | 
			
		||||
        } else {
 | 
			
		||||
            out << s[i - 1];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (i == s.size()) {
 | 
			
		||||
        out << s[i - 1];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void printMultiLineString(Out & out, const Value & v)
 | 
			
		||||
{
 | 
			
		||||
    std::string s = v.string.s;
 | 
			
		||||
    Out strOut(out, "''", "''", Out::MULTI_LINE);
 | 
			
		||||
    std::string::size_type begin = 0;
 | 
			
		||||
    while (begin < s.size()) {
 | 
			
		||||
        std::string::size_type end = s.find('\n', begin);
 | 
			
		||||
        if (end == std::string::npos) {
 | 
			
		||||
            multiLineStringEscape(strOut, s.substr(begin, s.size() - begin));
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        multiLineStringEscape(strOut, s.substr(begin, end - begin));
 | 
			
		||||
        strOut << Out::sep;
 | 
			
		||||
        begin = end + 1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void printValue(Context & ctx, Out & out, std::variant<Value, std::exception_ptr> maybeValue, const std::string & path)
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        if (auto ex = std::get_if<std::exception_ptr>(&maybeValue)) {
 | 
			
		||||
            std::rethrow_exception(*ex);
 | 
			
		||||
        }
 | 
			
		||||
        Value v = evaluateValue(ctx, std::get<Value>(maybeValue));
 | 
			
		||||
        if (ctx.state.isDerivation(v)) {
 | 
			
		||||
            describeDerivation(ctx, out, v);
 | 
			
		||||
        } else if (v.isList()) {
 | 
			
		||||
            printList(ctx, out, v);
 | 
			
		||||
        } else if (v.type == tAttrs) {
 | 
			
		||||
            printAttrs(ctx, out, v, path);
 | 
			
		||||
        } else if (v.type == tString && std::string(v.string.s).find('\n') != std::string::npos) {
 | 
			
		||||
            printMultiLineString(out, v);
 | 
			
		||||
        } else {
 | 
			
		||||
            ctx.state.forceValueDeep(v);
 | 
			
		||||
            out << v;
 | 
			
		||||
        }
 | 
			
		||||
    } catch (ThrownError & e) {
 | 
			
		||||
        if (e.msg() == "The option `" + path + "' is used but not defined.") {
 | 
			
		||||
            // 93% of errors are this, and just letting this message through would be
 | 
			
		||||
            // misleading.  These values may or may not actually be "used" in the
 | 
			
		||||
            // config.  The thing throwing the error message assumes that if anything
 | 
			
		||||
            // ever looks at this value, it is a "use" of this value.  But here in
 | 
			
		||||
            // nixos-option, we are looking at this value only to print it.
 | 
			
		||||
            // In order to avoid implying that this undefined value is actually
 | 
			
		||||
            // referenced, eat the underlying error message and emit "«not defined»".
 | 
			
		||||
            out << "«not defined»";
 | 
			
		||||
        } else {
 | 
			
		||||
            out << describeError(e);
 | 
			
		||||
        }
 | 
			
		||||
    } catch (Error & e) {
 | 
			
		||||
        out << describeError(e);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void printConfigValue(Context & ctx, Out & out, const std::string & path, std::variant<Value, std::exception_ptr> v)
 | 
			
		||||
{
 | 
			
		||||
    out << path << " = ";
 | 
			
		||||
    printValue(ctx, out, std::move(v), path);
 | 
			
		||||
    out << ";\n";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void printAll(Context & ctx, Out & out)
 | 
			
		||||
{
 | 
			
		||||
    mapOptions(
 | 
			
		||||
        [&ctx, &out](const std::string & optionPath) {
 | 
			
		||||
            mapConfigValuesInOption(
 | 
			
		||||
                [&ctx, &out](const std::string & configPath, std::variant<Value, std::exception_ptr> v) {
 | 
			
		||||
                    printConfigValue(ctx, out, configPath, v);
 | 
			
		||||
                },
 | 
			
		||||
                optionPath, ctx);
 | 
			
		||||
        },
 | 
			
		||||
        ctx, ctx.optionsRoot);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void printAttr(Context & ctx, Out & out, const std::string & path, Value & root)
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        printValue(ctx, out, *findAlongAttrPath(ctx.state, path, ctx.autoArgs, root), path);
 | 
			
		||||
    } catch (Error & e) {
 | 
			
		||||
        out << describeError(e);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool hasExample(Context & ctx, Value & option)
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        findAlongAttrPath(ctx.state, "example", ctx.autoArgs, option);
 | 
			
		||||
        return true;
 | 
			
		||||
    } catch (Error &) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void printOption(Context & ctx, Out & out, const std::string & path, Value & option)
 | 
			
		||||
{
 | 
			
		||||
    out << "Value:\n";
 | 
			
		||||
    printAttr(ctx, out, path, ctx.configRoot);
 | 
			
		||||
 | 
			
		||||
    out << "\n\nDefault:\n";
 | 
			
		||||
    printAttr(ctx, out, "default", option);
 | 
			
		||||
 | 
			
		||||
    out << "\n\nType:\n";
 | 
			
		||||
    printAttr(ctx, out, "type.description", option);
 | 
			
		||||
 | 
			
		||||
    if (hasExample(ctx, option)) {
 | 
			
		||||
        out << "\n\nExample:\n";
 | 
			
		||||
        printAttr(ctx, out, "example", option);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    out << "\n\nDescription:\n";
 | 
			
		||||
    printAttr(ctx, out, "description", option);
 | 
			
		||||
 | 
			
		||||
    out << "\n\nDeclared by:\n";
 | 
			
		||||
    printAttr(ctx, out, "declarations", option);
 | 
			
		||||
 | 
			
		||||
    out << "\n\nDefined by:\n";
 | 
			
		||||
    printAttr(ctx, out, "files", option);
 | 
			
		||||
    out << "\n";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void printListing(Out & out, Value & v)
 | 
			
		||||
{
 | 
			
		||||
    out << "This attribute set contains:\n";
 | 
			
		||||
    for (const auto & a : v.attrs->lexicographicOrder()) {
 | 
			
		||||
        std::string name = a->name;
 | 
			
		||||
        if (!name.empty() && name[0] != '_') {
 | 
			
		||||
            out << name << "\n";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool optionTypeIs(Context & ctx, Value & v, const std::string & soughtType)
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        const auto & typeLookup = v.attrs->find(ctx.state.sType);
 | 
			
		||||
        if (typeLookup == v.attrs->end()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        Value type = evaluateValue(ctx, *typeLookup->value);
 | 
			
		||||
        if (type.type != tAttrs) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        const auto & nameLookup = type.attrs->find(ctx.state.sName);
 | 
			
		||||
        if (nameLookup == type.attrs->end()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        Value name = evaluateValue(ctx, *nameLookup->value);
 | 
			
		||||
        if (name.type != tString) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        return name.string.s == soughtType;
 | 
			
		||||
    } catch (Error &) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool isAggregateOptionType(Context & ctx, Value & v)
 | 
			
		||||
{
 | 
			
		||||
    return optionTypeIs(ctx, v, "attrsOf") || optionTypeIs(ctx, v, "listOf") || optionTypeIs(ctx, v, "loaOf");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MakeError(OptionPathError, EvalError);
 | 
			
		||||
 | 
			
		||||
Value getSubOptions(Context & ctx, Value & option)
 | 
			
		||||
{
 | 
			
		||||
    Value getSubOptions = evaluateValue(ctx, *findAlongAttrPath(ctx.state, "type.getSubOptions", ctx.autoArgs, option));
 | 
			
		||||
    if (getSubOptions.type != tLambda) {
 | 
			
		||||
        throw OptionPathError("Option's type.getSubOptions isn't a function");
 | 
			
		||||
    }
 | 
			
		||||
    Value emptyString{};
 | 
			
		||||
    nix::mkString(emptyString, "");
 | 
			
		||||
    Value v;
 | 
			
		||||
    ctx.state.callFunction(getSubOptions, emptyString, v, nix::Pos{});
 | 
			
		||||
    return v;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Carefully walk an option path, looking for sub-options when a path walks past
 | 
			
		||||
// an option value.
 | 
			
		||||
Value findAlongOptionPath(Context & ctx, const std::string & path)
 | 
			
		||||
{
 | 
			
		||||
    Strings tokens = parseAttrPath(path);
 | 
			
		||||
    Value v = ctx.optionsRoot;
 | 
			
		||||
    for (auto i = tokens.begin(); i != tokens.end(); i++) {
 | 
			
		||||
        const auto & attr = *i;
 | 
			
		||||
        try {
 | 
			
		||||
            bool lastAttribute = std::next(i) == tokens.end();
 | 
			
		||||
            v = evaluateValue(ctx, v);
 | 
			
		||||
            if (attr.empty()) {
 | 
			
		||||
                throw OptionPathError("empty attribute name");
 | 
			
		||||
            }
 | 
			
		||||
            if (isOption(ctx, v) && optionTypeIs(ctx, v, "submodule")) {
 | 
			
		||||
                v = getSubOptions(ctx, v);
 | 
			
		||||
            }
 | 
			
		||||
            if (isOption(ctx, v) && isAggregateOptionType(ctx, v) && !lastAttribute) {
 | 
			
		||||
                v = getSubOptions(ctx, v);
 | 
			
		||||
                // Note that we've consumed attr, but didn't actually use it.  This is the path component that's looked
 | 
			
		||||
                // up in the list or attribute set that doesn't name an option -- the "root" in "users.users.root.name".
 | 
			
		||||
            } else if (v.type != tAttrs) {
 | 
			
		||||
                throw OptionPathError("Value is %s while a set was expected", showType(v));
 | 
			
		||||
            } else {
 | 
			
		||||
                const auto & next = v.attrs->find(ctx.state.symbols.create(attr));
 | 
			
		||||
                if (next == v.attrs->end()) {
 | 
			
		||||
                    throw OptionPathError("Attribute not found", attr, path);
 | 
			
		||||
                }
 | 
			
		||||
                v = *next->value;
 | 
			
		||||
            }
 | 
			
		||||
        } catch (OptionPathError & e) {
 | 
			
		||||
            throw OptionPathError("At '%s' in path '%s': %s", attr, path, e.msg());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return v;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void printOne(Context & ctx, Out & out, const std::string & path)
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        Value option = findAlongOptionPath(ctx, path);
 | 
			
		||||
        option = evaluateValue(ctx, option);
 | 
			
		||||
        if (isOption(ctx, option)) {
 | 
			
		||||
            printOption(ctx, out, path, option);
 | 
			
		||||
        } else {
 | 
			
		||||
            printListing(out, option);
 | 
			
		||||
        }
 | 
			
		||||
    } catch (Error & e) {
 | 
			
		||||
        std::cerr << "error: " << e.msg()
 | 
			
		||||
                  << "\nAn error occurred while looking for attribute names. Are "
 | 
			
		||||
                     "you sure that '"
 | 
			
		||||
                  << path << "' exists?\n";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char ** argv)
 | 
			
		||||
{
 | 
			
		||||
    bool all = false;
 | 
			
		||||
    std::string path = ".";
 | 
			
		||||
    std::string optionsExpr = "(import <nixpkgs/nixos> {}).options";
 | 
			
		||||
    std::string configExpr = "(import <nixpkgs/nixos> {}).config";
 | 
			
		||||
    std::vector<std::string> args;
 | 
			
		||||
 | 
			
		||||
    struct MyArgs : nix::LegacyArgs, nix::MixEvalArgs
 | 
			
		||||
    {
 | 
			
		||||
        using nix::LegacyArgs::LegacyArgs;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MyArgs myArgs(nix::baseNameOf(argv[0]), [&](Strings::iterator & arg, const Strings::iterator & end) {
 | 
			
		||||
        if (*arg == "--help") {
 | 
			
		||||
            nix::showManPage("nixos-option");
 | 
			
		||||
        } else if (*arg == "--version") {
 | 
			
		||||
            nix::printVersion("nixos-option");
 | 
			
		||||
        } else if (*arg == "--all") {
 | 
			
		||||
            all = true;
 | 
			
		||||
        } else if (*arg == "--path") {
 | 
			
		||||
            path = nix::getArg(*arg, arg, end);
 | 
			
		||||
        } else if (*arg == "--options_expr") {
 | 
			
		||||
            optionsExpr = nix::getArg(*arg, arg, end);
 | 
			
		||||
        } else if (*arg == "--config_expr") {
 | 
			
		||||
            configExpr = nix::getArg(*arg, arg, end);
 | 
			
		||||
        } else if (!arg->empty() && arg->at(0) == '-') {
 | 
			
		||||
            return false;
 | 
			
		||||
        } else {
 | 
			
		||||
            args.push_back(*arg);
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    myArgs.parseCmdline(nix::argvToStrings(argc, argv));
 | 
			
		||||
 | 
			
		||||
    nix::initPlugins();
 | 
			
		||||
    nix::initGC();
 | 
			
		||||
    nix::settings.readOnlyMode = true;
 | 
			
		||||
    auto store = nix::openStore();
 | 
			
		||||
    auto state = std::make_unique<EvalState>(myArgs.searchPath, store);
 | 
			
		||||
 | 
			
		||||
    Value optionsRoot = parseAndEval(*state, optionsExpr, path);
 | 
			
		||||
    Value configRoot = parseAndEval(*state, configExpr, path);
 | 
			
		||||
 | 
			
		||||
    Context ctx{*state, *myArgs.getAutoArgs(*state), optionsRoot, configRoot};
 | 
			
		||||
    Out out(std::cout);
 | 
			
		||||
 | 
			
		||||
    if (all) {
 | 
			
		||||
        if (!args.empty()) {
 | 
			
		||||
            throw UsageError("--all cannot be used with arguments");
 | 
			
		||||
        }
 | 
			
		||||
        printAll(ctx, out);
 | 
			
		||||
    } else {
 | 
			
		||||
        if (args.empty()) {
 | 
			
		||||
            printOne(ctx, out, "");
 | 
			
		||||
        }
 | 
			
		||||
        for (const auto & arg : args) {
 | 
			
		||||
            printOne(ctx, out, arg);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ctx.state.printStats();
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@ -41,10 +41,7 @@ let
 | 
			
		||||
    inherit (config.system.nixos-generate-config) configuration;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  nixos-option = makeProg {
 | 
			
		||||
    name = "nixos-option";
 | 
			
		||||
    src = ./nixos-option.sh;
 | 
			
		||||
  };
 | 
			
		||||
  nixos-option = pkgs.callPackage ./nixos-option { };
 | 
			
		||||
 | 
			
		||||
  nixos-version = makeProg {
 | 
			
		||||
    name = "nixos-version";
 | 
			
		||||
 | 
			
		||||
@ -44,6 +44,7 @@
 | 
			
		||||
  ./hardware/all-firmware.nix
 | 
			
		||||
  ./hardware/bladeRF.nix
 | 
			
		||||
  ./hardware/brightnessctl.nix
 | 
			
		||||
  ./hardware/brillo.nix
 | 
			
		||||
  ./hardware/ckb-next.nix
 | 
			
		||||
  ./hardware/cpu/amd-microcode.nix
 | 
			
		||||
  ./hardware/cpu/intel-microcode.nix
 | 
			
		||||
@ -812,8 +813,10 @@
 | 
			
		||||
  ./services/web-apps/nexus.nix
 | 
			
		||||
  ./services/web-apps/pgpkeyserver-lite.nix
 | 
			
		||||
  ./services/web-apps/matomo.nix
 | 
			
		||||
  ./services/web-apps/moinmoin.nix
 | 
			
		||||
  ./services/web-apps/restya-board.nix
 | 
			
		||||
  ./services/web-apps/tt-rss.nix
 | 
			
		||||
  ./services/web-apps/trac.nix
 | 
			
		||||
  ./services/web-apps/selfoss.nix
 | 
			
		||||
  ./services/web-apps/shiori.nix
 | 
			
		||||
  ./services/web-apps/virtlyst.nix
 | 
			
		||||
@ -864,6 +867,7 @@
 | 
			
		||||
  ./services/x11/hardware/multitouch.nix
 | 
			
		||||
  ./services/x11/hardware/synaptics.nix
 | 
			
		||||
  ./services/x11/hardware/wacom.nix
 | 
			
		||||
  ./services/x11/hardware/digimend.nix
 | 
			
		||||
  ./services/x11/hardware/cmt.nix
 | 
			
		||||
  ./services/x11/gdk-pixbuf.nix
 | 
			
		||||
  ./services/x11/redshift.nix
 | 
			
		||||
 | 
			
		||||
@ -115,6 +115,16 @@ in
 | 
			
		||||
        '';
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      agentPKCS11Whitelist = mkOption {
 | 
			
		||||
        type = types.nullOr types.str;
 | 
			
		||||
        default = null;
 | 
			
		||||
        example = "\${pkgs.opensc}/lib/opensc-pkcs11.so";
 | 
			
		||||
        description = ''
 | 
			
		||||
          A pattern-list of acceptable paths for PKCS#11 shared libraries
 | 
			
		||||
          that may be used with the -s option to ssh-add.
 | 
			
		||||
        '';
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      package = mkOption {
 | 
			
		||||
        type = types.package;
 | 
			
		||||
        default = pkgs.openssh;
 | 
			
		||||
@ -241,6 +251,7 @@ in
 | 
			
		||||
            ExecStart =
 | 
			
		||||
                "${cfg.package}/bin/ssh-agent " +
 | 
			
		||||
                optionalString (cfg.agentTimeout != null) ("-t ${cfg.agentTimeout} ") +
 | 
			
		||||
                optionalString (cfg.agentPKCS11Whitelist != null) ("-P ${cfg.agentPKCS11Whitelist} ")
 | 
			
		||||
                "-a %t/ssh-agent";
 | 
			
		||||
            StandardOutput = "null";
 | 
			
		||||
            Type = "forking";
 | 
			
		||||
 | 
			
		||||
@ -50,9 +50,6 @@ in
 | 
			
		||||
          <pam_mount>
 | 
			
		||||
          <debug enable="0" />
 | 
			
		||||
 | 
			
		||||
          ${concatStrings (map userVolumeEntry (attrValues extraUserVolumes))}
 | 
			
		||||
          ${concatStringsSep "\n" cfg.extraVolumes}
 | 
			
		||||
 | 
			
		||||
          <!-- if activated, requires ofl from hxtools to be present -->
 | 
			
		||||
          <logout wait="0" hup="no" term="no" kill="no" />
 | 
			
		||||
          <!-- set PATH variable for pam_mount module -->
 | 
			
		||||
@ -64,6 +61,9 @@ in
 | 
			
		||||
          <cryptmount>${pkgs.pam_mount}/bin/mount.crypt %(VOLUME) %(MNTPT)</cryptmount>
 | 
			
		||||
          <cryptumount>${pkgs.pam_mount}/bin/umount.crypt %(MNTPT)</cryptumount>
 | 
			
		||||
          <pmvarrun>${pkgs.pam_mount}/bin/pmvarrun -u %(USER) -o %(OPERATION)</pmvarrun>
 | 
			
		||||
 | 
			
		||||
          ${concatStrings (map userVolumeEntry (attrValues extraUserVolumes))}
 | 
			
		||||
          ${concatStringsSep "\n" cfg.extraVolumes}
 | 
			
		||||
          </pam_mount>
 | 
			
		||||
          '';
 | 
			
		||||
    }];
 | 
			
		||||
 | 
			
		||||
@ -181,6 +181,7 @@ in {
 | 
			
		||||
        ProtectKernelModules = true;
 | 
			
		||||
        RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX AF_NETLINK";
 | 
			
		||||
        RestrictNamespaces = true;
 | 
			
		||||
        Restart = "always";
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -138,7 +138,7 @@ in {
 | 
			
		||||
      description = "Real time performance monitoring";
 | 
			
		||||
      after = [ "network.target" ];
 | 
			
		||||
      wantedBy = [ "multi-user.target" ];
 | 
			
		||||
      path = (with pkgs; [ gawk curl ]) ++ lib.optional cfg.python.enable
 | 
			
		||||
      path = (with pkgs; [ curl gawk which ]) ++ lib.optional cfg.python.enable
 | 
			
		||||
        (pkgs.python3.withPackages cfg.python.extraPackages);
 | 
			
		||||
      serviceConfig = {
 | 
			
		||||
        Environment="PYTHONPATH=${pkgs.netdata}/libexec/netdata/python.d/python_modules";
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,7 @@ let
 | 
			
		||||
    iptables -w -t nat -N nixos-nat-post
 | 
			
		||||
 | 
			
		||||
    # We can't match on incoming interface in POSTROUTING, so
 | 
			
		||||
    # mark packets coming from the external interfaces.
 | 
			
		||||
    # mark packets coming from the internal interfaces.
 | 
			
		||||
    ${concatMapStrings (iface: ''
 | 
			
		||||
      iptables -w -t nat -A nixos-nat-pre \
 | 
			
		||||
        -i '${iface}' -j MARK --set-mark 1
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										303
									
								
								nixos/modules/services/web-apps/moinmoin.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										303
									
								
								nixos/modules/services/web-apps/moinmoin.nix
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,303 @@
 | 
			
		||||
{ config, lib, pkgs, ... }:
 | 
			
		||||
with lib;
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
  cfg = config.services.moinmoin;
 | 
			
		||||
  python = pkgs.python27;
 | 
			
		||||
  pkg = python.pkgs.moinmoin;
 | 
			
		||||
  dataDir = "/var/lib/moin";
 | 
			
		||||
  usingGunicorn = cfg.webServer == "nginx-gunicorn" || cfg.webServer == "gunicorn";
 | 
			
		||||
  usingNginx = cfg.webServer == "nginx-gunicorn";
 | 
			
		||||
  user = "moin";
 | 
			
		||||
  group = "moin";
 | 
			
		||||
 | 
			
		||||
  uLit = s: ''u"${s}"'';
 | 
			
		||||
  indentLines = n: str: concatMapStrings (line: "${fixedWidthString n " " " "}${line}\n") (splitString "\n" str);
 | 
			
		||||
 | 
			
		||||
  moinCliWrapper = wikiIdent: pkgs.writeShellScriptBin "moin-${wikiIdent}" ''
 | 
			
		||||
    ${pkgs.su}/bin/su -s ${pkgs.runtimeShell} -c "${pkg}/bin/moin --config-dir=/var/lib/moin/${wikiIdent}/config $*" ${user}
 | 
			
		||||
  '';
 | 
			
		||||
 | 
			
		||||
  wikiConfig = wikiIdent: w: ''
 | 
			
		||||
    # -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
    from MoinMoin.config import multiconfig, url_prefix_static
 | 
			
		||||
 | 
			
		||||
    class Config(multiconfig.DefaultConfig):
 | 
			
		||||
        ${optionalString (w.webLocation != "/") ''
 | 
			
		||||
          url_prefix_static = '${w.webLocation}' + url_prefix_static
 | 
			
		||||
        ''}
 | 
			
		||||
 | 
			
		||||
        sitename = u'${w.siteName}'
 | 
			
		||||
        page_front_page = u'${w.frontPage}'
 | 
			
		||||
 | 
			
		||||
        data_dir = '${dataDir}/${wikiIdent}/data'
 | 
			
		||||
        data_underlay_dir = '${dataDir}/${wikiIdent}/underlay'
 | 
			
		||||
 | 
			
		||||
        language_default = u'${w.languageDefault}'
 | 
			
		||||
        ${optionalString (w.superUsers != []) ''
 | 
			
		||||
          superuser = [${concatMapStringsSep ", " uLit w.superUsers}]
 | 
			
		||||
        ''}
 | 
			
		||||
 | 
			
		||||
    ${indentLines 4 w.extraConfig}
 | 
			
		||||
  '';
 | 
			
		||||
  wikiConfigFile = name: wiki: pkgs.writeText "${name}.py" (wikiConfig name wiki);
 | 
			
		||||
 | 
			
		||||
in
 | 
			
		||||
{
 | 
			
		||||
  options.services.moinmoin = with types; {
 | 
			
		||||
    enable = mkEnableOption "MoinMoin Wiki Engine";
 | 
			
		||||
 | 
			
		||||
    webServer = mkOption {
 | 
			
		||||
      type = enum [ "nginx-gunicorn" "gunicorn" "none" ];
 | 
			
		||||
      default = "nginx-gunicorn";
 | 
			
		||||
      example = "none";
 | 
			
		||||
      description = ''
 | 
			
		||||
        Which web server to use to serve the wiki.
 | 
			
		||||
        Use <literal>none</literal> if you want to configure this yourself.
 | 
			
		||||
      '';
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    gunicorn.workers = mkOption {
 | 
			
		||||
      type = ints.positive;
 | 
			
		||||
      default = 3;
 | 
			
		||||
      example = 10;
 | 
			
		||||
      description = ''
 | 
			
		||||
        The number of worker processes for handling requests.
 | 
			
		||||
      '';
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    wikis = mkOption {
 | 
			
		||||
      type = attrsOf (submodule ({ name, ... }: {
 | 
			
		||||
        options = {
 | 
			
		||||
          siteName = mkOption {
 | 
			
		||||
            type = str;
 | 
			
		||||
            default = "Untitled Wiki";
 | 
			
		||||
            example = "ExampleWiki";
 | 
			
		||||
            description = ''
 | 
			
		||||
              Short description of your wiki site, displayed below the logo on each page, and
 | 
			
		||||
              used in RSS documents as the channel title.
 | 
			
		||||
            '';
 | 
			
		||||
          };
 | 
			
		||||
 | 
			
		||||
          webHost = mkOption {
 | 
			
		||||
            type = str;
 | 
			
		||||
            description = "Host part of the wiki URL. If undefined, the name of the attribute set will be used.";
 | 
			
		||||
            example = "wiki.example.org";
 | 
			
		||||
          };
 | 
			
		||||
 | 
			
		||||
          webLocation = mkOption {
 | 
			
		||||
            type = str;
 | 
			
		||||
            default = "/";
 | 
			
		||||
            example = "/moin";
 | 
			
		||||
            description = "Location part of the wiki URL.";
 | 
			
		||||
          };
 | 
			
		||||
 | 
			
		||||
          frontPage = mkOption {
 | 
			
		||||
            type = str;
 | 
			
		||||
            default = "LanguageSetup";
 | 
			
		||||
            example = "FrontPage";
 | 
			
		||||
            description = ''
 | 
			
		||||
              Front page name. Set this to something like <literal>FrontPage</literal> once languages are
 | 
			
		||||
              configured.
 | 
			
		||||
            '';
 | 
			
		||||
          };
 | 
			
		||||
 | 
			
		||||
          superUsers = mkOption {
 | 
			
		||||
            type = listOf str;
 | 
			
		||||
            default = [];
 | 
			
		||||
            example = [ "elvis" ];
 | 
			
		||||
            description = ''
 | 
			
		||||
              List of trusted user names with wiki system administration super powers.
 | 
			
		||||
 | 
			
		||||
              Please note that accounts for these users need to be created using the <command>moin</command> command-line utility, e.g.:
 | 
			
		||||
              <command>moin-<replaceable>WIKINAME</replaceable> account create --name=<replaceable>NAME</replaceable> --email=<replaceable>EMAIL</replaceable> --password=<replaceable>PASSWORD</replaceable></command>.
 | 
			
		||||
            '';
 | 
			
		||||
          };
 | 
			
		||||
 | 
			
		||||
          languageDefault = mkOption {
 | 
			
		||||
            type = str;
 | 
			
		||||
            default = "en";
 | 
			
		||||
            example = "de";
 | 
			
		||||
            description = "The ISO-639-1 name of the main wiki language. Languages that MoinMoin does not support are ignored.";
 | 
			
		||||
          };
 | 
			
		||||
 | 
			
		||||
          extraConfig = mkOption {
 | 
			
		||||
            type = lines;
 | 
			
		||||
            default = "";
 | 
			
		||||
            example = ''
 | 
			
		||||
              show_hosts = True
 | 
			
		||||
              search_results_per_page = 100
 | 
			
		||||
              acl_rights_default = u"Known:read,write,delete,revert All:read"
 | 
			
		||||
              logo_string = u"<h2>\U0001f639</h2>"
 | 
			
		||||
              theme_default = u"modernized"
 | 
			
		||||
 | 
			
		||||
              user_checkbox_defaults = {'show_page_trail': 0, 'edit_on_doubleclick': 0}
 | 
			
		||||
              navi_bar = [u'SomePage'] + multiconfig.DefaultConfig.navi_bar
 | 
			
		||||
              actions_excluded = multiconfig.DefaultConfig.actions_excluded + ['newaccount']
 | 
			
		||||
 | 
			
		||||
              mail_smarthost = "mail.example.org"
 | 
			
		||||
              mail_from = u"Example.Org Wiki <wiki@example.org>"
 | 
			
		||||
            '';
 | 
			
		||||
            description = ''
 | 
			
		||||
              Additional configuration to be appended verbatim to this wiki's config.
 | 
			
		||||
 | 
			
		||||
              See <link xlink:href='http://moinmo.in/HelpOnConfiguration' /> for documentation.
 | 
			
		||||
            '';
 | 
			
		||||
          };
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
        config = {
 | 
			
		||||
          webHost = mkDefault name;
 | 
			
		||||
        };
 | 
			
		||||
      }));
 | 
			
		||||
      example = literalExample ''
 | 
			
		||||
        {
 | 
			
		||||
          "mywiki" = {
 | 
			
		||||
            siteName = "Example Wiki";
 | 
			
		||||
            webHost = "wiki.example.org";
 | 
			
		||||
            superUsers = [ "admin" ];
 | 
			
		||||
            frontPage = "Index";
 | 
			
		||||
            extraConfig = "page_category_regex = ur'(?P<all>(Category|Kategorie)(?P<key>(?!Template)\S+))'"
 | 
			
		||||
          };
 | 
			
		||||
        }
 | 
			
		||||
      '';
 | 
			
		||||
      description = ''
 | 
			
		||||
        Configurations of the individual wikis. Attribute names must be valid Python
 | 
			
		||||
        identifiers of the form <literal>[A-Za-z_][A-Za-z0-9_]*</literal>.
 | 
			
		||||
 | 
			
		||||
        For every attribute <replaceable>WIKINAME</replaceable>, a helper script
 | 
			
		||||
        moin-<replaceable>WIKINAME</replaceable> is created which runs the
 | 
			
		||||
        <command>moin</command> command under the <literal>moin</literal> user (to avoid
 | 
			
		||||
        file ownership issues) and with the right configuration directory passed to it.
 | 
			
		||||
      '';
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  config = mkIf cfg.enable {
 | 
			
		||||
    assertions = forEach (attrNames cfg.wikis) (wname:
 | 
			
		||||
      { assertion = builtins.match "[A-Za-z_][A-Za-z0-9_]*" wname != null;
 | 
			
		||||
        message = "${wname} is not valid Python identifier";
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    users.users = {
 | 
			
		||||
      moin = {
 | 
			
		||||
        description = "MoinMoin wiki";
 | 
			
		||||
        home = dataDir;
 | 
			
		||||
        group = group;
 | 
			
		||||
        isSystemUser = true;
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    users.groups = {
 | 
			
		||||
      moin = {
 | 
			
		||||
        members = mkIf usingNginx [ config.services.nginx.user ];
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    environment.systemPackages = [ pkg ] ++ map moinCliWrapper (attrNames cfg.wikis);
 | 
			
		||||
 | 
			
		||||
    systemd.services = mkIf usingGunicorn
 | 
			
		||||
      (flip mapAttrs' cfg.wikis (wikiIdent: wiki:
 | 
			
		||||
        nameValuePair "moin-${wikiIdent}"
 | 
			
		||||
          {
 | 
			
		||||
            description = "MoinMoin wiki ${wikiIdent} - gunicorn process";
 | 
			
		||||
            wantedBy = [ "multi-user.target" ];
 | 
			
		||||
            after = [ "network.target" ];
 | 
			
		||||
            restartIfChanged = true;
 | 
			
		||||
            restartTriggers = [ (wikiConfigFile wikiIdent wiki) ];
 | 
			
		||||
 | 
			
		||||
            environment = let
 | 
			
		||||
              penv = python.buildEnv.override {
 | 
			
		||||
                # setuptools: https://github.com/benoitc/gunicorn/issues/1716
 | 
			
		||||
                extraLibs = [ python.pkgs.gevent python.pkgs.setuptools pkg ];
 | 
			
		||||
              };
 | 
			
		||||
            in {
 | 
			
		||||
              PYTHONPATH = "${dataDir}/${wikiIdent}/config:${penv}/${python.sitePackages}";
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            preStart = ''
 | 
			
		||||
              umask 0007
 | 
			
		||||
              rm -rf ${dataDir}/${wikiIdent}/underlay
 | 
			
		||||
              cp -r ${pkg}/share/moin/underlay ${dataDir}/${wikiIdent}/
 | 
			
		||||
              chmod -R u+w ${dataDir}/${wikiIdent}/underlay
 | 
			
		||||
            '';
 | 
			
		||||
 | 
			
		||||
            serviceConfig = {
 | 
			
		||||
              User = user;
 | 
			
		||||
              Group = group;
 | 
			
		||||
              WorkingDirectory = "${dataDir}/${wikiIdent}";
 | 
			
		||||
              ExecStart = ''${python.pkgs.gunicorn}/bin/gunicorn moin_wsgi \
 | 
			
		||||
                --name gunicorn-${wikiIdent} \
 | 
			
		||||
                --workers ${toString cfg.gunicorn.workers} \
 | 
			
		||||
                --worker-class gevent \
 | 
			
		||||
                --bind unix:/run/moin/${wikiIdent}/gunicorn.sock
 | 
			
		||||
              '';
 | 
			
		||||
 | 
			
		||||
              Restart = "on-failure";
 | 
			
		||||
              RestartSec = "2s";
 | 
			
		||||
              StartLimitIntervalSec = "30s";
 | 
			
		||||
 | 
			
		||||
              StateDirectory = "moin/${wikiIdent}";
 | 
			
		||||
              StateDirectoryMode = "0750";
 | 
			
		||||
              RuntimeDirectory = "moin/${wikiIdent}";
 | 
			
		||||
              RuntimeDirectoryMode = "0750";
 | 
			
		||||
 | 
			
		||||
              NoNewPrivileges = true;
 | 
			
		||||
              ProtectSystem = "strict";
 | 
			
		||||
              ProtectHome = true;
 | 
			
		||||
              PrivateTmp = true;
 | 
			
		||||
              PrivateDevices = true;
 | 
			
		||||
              PrivateNetwork = true;
 | 
			
		||||
              ProtectKernelTunables = true;
 | 
			
		||||
              ProtectKernelModules = true;
 | 
			
		||||
              ProtectControlGroups = true;
 | 
			
		||||
              RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
 | 
			
		||||
              RestrictNamespaces = true;
 | 
			
		||||
              LockPersonality = true;
 | 
			
		||||
              MemoryDenyWriteExecute = true;
 | 
			
		||||
              RestrictRealtime = true;
 | 
			
		||||
            };
 | 
			
		||||
          }
 | 
			
		||||
      ));
 | 
			
		||||
 | 
			
		||||
    services.nginx = mkIf usingNginx {
 | 
			
		||||
      enable = true;
 | 
			
		||||
      virtualHosts = flip mapAttrs' cfg.wikis (name: w: nameValuePair w.webHost {
 | 
			
		||||
        forceSSL = mkDefault true;
 | 
			
		||||
        enableACME = mkDefault true;
 | 
			
		||||
        locations."${w.webLocation}" = {
 | 
			
		||||
          extraConfig = ''
 | 
			
		||||
            proxy_set_header Host $host;
 | 
			
		||||
            proxy_set_header X-Real-IP $remote_addr;
 | 
			
		||||
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 | 
			
		||||
            proxy_set_header X-Forwarded-Proto $scheme;
 | 
			
		||||
            proxy_set_header X-Forwarded-Host $host;
 | 
			
		||||
            proxy_set_header X-Forwarded-Server $host;
 | 
			
		||||
 | 
			
		||||
            proxy_pass http://unix:/run/moin/${name}/gunicorn.sock;
 | 
			
		||||
          '';
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    systemd.tmpfiles.rules = [
 | 
			
		||||
      "d  /run/moin            0750 ${user} ${group} - -"
 | 
			
		||||
      "d  ${dataDir}           0550 ${user} ${group} - -"
 | 
			
		||||
    ]
 | 
			
		||||
    ++ (concatLists (flip mapAttrsToList cfg.wikis (wikiIdent: wiki: [
 | 
			
		||||
      "d  ${dataDir}/${wikiIdent}                      0750 ${user} ${group} - -"
 | 
			
		||||
      "d  ${dataDir}/${wikiIdent}/config               0550 ${user} ${group} - -"
 | 
			
		||||
      "L+ ${dataDir}/${wikiIdent}/config/wikiconfig.py -    -       -        - ${wikiConfigFile wikiIdent wiki}"
 | 
			
		||||
      # needed in order to pass module name to gunicorn
 | 
			
		||||
      "L+ ${dataDir}/${wikiIdent}/config/moin_wsgi.py  -    -       -        - ${pkg}/share/moin/server/moin.wsgi"
 | 
			
		||||
      # seed data files
 | 
			
		||||
      "C  ${dataDir}/${wikiIdent}/data                 0770 ${user} ${group} - ${pkg}/share/moin/data"
 | 
			
		||||
      # fix nix store permissions
 | 
			
		||||
      "Z  ${dataDir}/${wikiIdent}/data                 0770 ${user} ${group} - -"
 | 
			
		||||
    ])));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  meta.maintainers = with lib.maintainers; [ b42 ];
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								nixos/modules/services/web-apps/trac.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								nixos/modules/services/web-apps/trac.nix
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,79 @@
 | 
			
		||||
{ config, lib, pkgs, ... }:
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
  cfg = config.services.trac;
 | 
			
		||||
 | 
			
		||||
  inherit (lib) mkEnableOption mkIf mkOption types;
 | 
			
		||||
 | 
			
		||||
in {
 | 
			
		||||
 | 
			
		||||
  options = {
 | 
			
		||||
 | 
			
		||||
    services.trac = {
 | 
			
		||||
      enable = mkEnableOption "Trac service";
 | 
			
		||||
 | 
			
		||||
      listen = {
 | 
			
		||||
        ip = mkOption {
 | 
			
		||||
          type = types.str;
 | 
			
		||||
          default = "0.0.0.0";
 | 
			
		||||
          description = ''
 | 
			
		||||
            IP address that Trac should listen on.
 | 
			
		||||
          '';
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        port = mkOption {
 | 
			
		||||
          type = types.port;
 | 
			
		||||
          default = 8000;
 | 
			
		||||
          description = ''
 | 
			
		||||
            Listen port for Trac.
 | 
			
		||||
          '';
 | 
			
		||||
        };
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      dataDir = mkOption {
 | 
			
		||||
        default = "/var/lib/trac";
 | 
			
		||||
        type = types.path;
 | 
			
		||||
        description = ''
 | 
			
		||||
            The directory for storing the Trac data.
 | 
			
		||||
        '';
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      openFirewall = mkOption {
 | 
			
		||||
        type = types.bool;
 | 
			
		||||
        default = false;
 | 
			
		||||
        description = ''
 | 
			
		||||
          Open ports in the firewall for Trac.
 | 
			
		||||
        '';
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  config = mkIf cfg.enable {
 | 
			
		||||
 | 
			
		||||
    systemd.services.trac = {
 | 
			
		||||
      description = "Trac server";
 | 
			
		||||
      wantedBy = [ "multi-user.target" ];
 | 
			
		||||
      serviceConfig = {
 | 
			
		||||
        DynamicUser = true;
 | 
			
		||||
        StateDirectory = baseNameOf cfg.dataDir;
 | 
			
		||||
        ExecStart = ''
 | 
			
		||||
          ${pkgs.trac}/bin/tracd -s \
 | 
			
		||||
            -b ${toString cfg.listen.ip} \
 | 
			
		||||
            -p ${toString cfg.listen.port} \
 | 
			
		||||
            ${cfg.dataDir}
 | 
			
		||||
        '';
 | 
			
		||||
      };
 | 
			
		||||
      preStart = ''
 | 
			
		||||
        if [ ! -e ${cfg.dataDir}/VERSION ]; then
 | 
			
		||||
          ${pkgs.trac}/bin/trac-admin ${cfg.dataDir} initenv Trac "sqlite:db/trac.db"
 | 
			
		||||
        fi
 | 
			
		||||
      '';
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    networking.firewall = mkIf cfg.openFirewall {
 | 
			
		||||
      allowedTCPPorts = [ cfg.listen.port ];
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								nixos/modules/services/x11/hardware/digimend.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								nixos/modules/services/x11/hardware/digimend.nix
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,43 @@
 | 
			
		||||
{ config, lib, pkgs, ... }:
 | 
			
		||||
 | 
			
		||||
with lib;
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
 | 
			
		||||
  cfg = config.services.xserver.digimend;
 | 
			
		||||
 | 
			
		||||
  pkg = config.boot.kernelPackages.digimend;
 | 
			
		||||
 | 
			
		||||
in
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  options = {
 | 
			
		||||
 | 
			
		||||
    services.xserver.digimend = {
 | 
			
		||||
 | 
			
		||||
      enable = mkOption {
 | 
			
		||||
        default = false;
 | 
			
		||||
        description = ''
 | 
			
		||||
          Whether to enable the digimend drivers for Huion/XP-Pen/etc. tablets.
 | 
			
		||||
        '';
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  config = mkIf cfg.enable {
 | 
			
		||||
 | 
			
		||||
    # digimend drivers use xsetwacom and wacom X11 drivers
 | 
			
		||||
    services.xserver.wacom.enable = true;
 | 
			
		||||
 | 
			
		||||
    boot.extraModulePackages = [ pkg ];
 | 
			
		||||
 | 
			
		||||
    environment.etc."X11/xorg.conf.d/50-digimend.conf".source =
 | 
			
		||||
      "${pkg}/usr/share/X11/xorg.conf.d/50-digimend.conf";
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -23,24 +23,56 @@ let
 | 
			
		||||
 | 
			
		||||
  cfg = config.virtualisation;
 | 
			
		||||
 | 
			
		||||
  qemuGraphics = lib.optionalString (!cfg.graphics) "-nographic";
 | 
			
		||||
 | 
			
		||||
  consoles = lib.concatMapStringsSep " " (c: "console=${c}") cfg.qemu.consoles;
 | 
			
		||||
 | 
			
		||||
  # XXX: This is very ugly and in the future we really should use attribute
 | 
			
		||||
  # sets to build ALL of the QEMU flags instead of this mixed mess of Nix
 | 
			
		||||
  # expressions and shell script stuff.
 | 
			
		||||
  mkDiskIfaceDriveFlag = idx: driveArgs: let
 | 
			
		||||
    inherit (cfg.qemu) diskInterface;
 | 
			
		||||
    # The drive identifier created by incrementing the index by one using the
 | 
			
		||||
    # shell.
 | 
			
		||||
    drvId = "drive$((${idx} + 1))";
 | 
			
		||||
    # NOTE: DO NOT shell escape, because this may contain shell variables.
 | 
			
		||||
    commonArgs = "index=${idx},id=${drvId},${driveArgs}";
 | 
			
		||||
    isSCSI = diskInterface == "scsi";
 | 
			
		||||
    devArgs = "${diskInterface}-hd,drive=${drvId}";
 | 
			
		||||
    args = "-drive ${commonArgs},if=none -device lsi53c895a -device ${devArgs}";
 | 
			
		||||
  in if isSCSI then args else "-drive ${commonArgs},if=${diskInterface}";
 | 
			
		||||
  driveOpts = { ... }: {
 | 
			
		||||
 | 
			
		||||
    options = {
 | 
			
		||||
 | 
			
		||||
      file = mkOption {
 | 
			
		||||
        type = types.str;
 | 
			
		||||
        description = "The file image used for this drive.";
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      driveExtraOpts = mkOption {
 | 
			
		||||
        type = types.attrsOf types.str;
 | 
			
		||||
        default = {};
 | 
			
		||||
        description = "Extra options passed to drive flag.";
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      deviceExtraOpts = mkOption {
 | 
			
		||||
        type = types.attrsOf types.str;
 | 
			
		||||
        default = {};
 | 
			
		||||
        description = "Extra options passed to device flag.";
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  driveCmdline = idx: { file, driveExtraOpts, deviceExtraOpts, ... }:
 | 
			
		||||
    let
 | 
			
		||||
      drvId = "drive${toString idx}";
 | 
			
		||||
      mkKeyValue = generators.mkKeyValueDefault {} "=";
 | 
			
		||||
      mkOpts = opts: concatStringsSep "," (mapAttrsToList mkKeyValue opts);
 | 
			
		||||
      driveOpts = mkOpts (driveExtraOpts // {
 | 
			
		||||
        index = idx;
 | 
			
		||||
        id = drvId;
 | 
			
		||||
        "if" = "none";
 | 
			
		||||
        inherit file;
 | 
			
		||||
      });
 | 
			
		||||
      deviceOpts = mkOpts (deviceExtraOpts // {
 | 
			
		||||
        drive = drvId;
 | 
			
		||||
      });
 | 
			
		||||
      device =
 | 
			
		||||
        if cfg.qemu.diskInterface == "scsi" then
 | 
			
		||||
          "-device lsi53c895a -device scsi-hd,${deviceOpts}"
 | 
			
		||||
        else
 | 
			
		||||
          "-device virtio-blk-pci,${deviceOpts}";
 | 
			
		||||
    in
 | 
			
		||||
      "-drive ${driveOpts} ${device}";
 | 
			
		||||
 | 
			
		||||
  drivesCmdLine = drives: concatStringsSep " " (imap1 driveCmdline drives);
 | 
			
		||||
 | 
			
		||||
  # Shell script to start the VM.
 | 
			
		||||
  startVM =
 | 
			
		||||
@ -77,13 +109,11 @@ let
 | 
			
		||||
      ''}
 | 
			
		||||
 | 
			
		||||
      cd $TMPDIR
 | 
			
		||||
      idx=2
 | 
			
		||||
      extraDisks=""
 | 
			
		||||
      idx=0
 | 
			
		||||
      ${flip concatMapStrings cfg.emptyDiskImages (size: ''
 | 
			
		||||
        if ! test -e "empty$idx.qcow2"; then
 | 
			
		||||
            ${qemu}/bin/qemu-img create -f qcow2 "empty$idx.qcow2" "${toString size}M"
 | 
			
		||||
        fi
 | 
			
		||||
        extraDisks="$extraDisks ${mkDiskIfaceDriveFlag "$idx" "file=$(pwd)/empty$idx.qcow2,werror=report"}"
 | 
			
		||||
        idx=$((idx + 1))
 | 
			
		||||
      '')}
 | 
			
		||||
 | 
			
		||||
@ -97,21 +127,7 @@ let
 | 
			
		||||
          -virtfs local,path=/nix/store,security_model=none,mount_tag=store \
 | 
			
		||||
          -virtfs local,path=$TMPDIR/xchg,security_model=none,mount_tag=xchg \
 | 
			
		||||
          -virtfs local,path=''${SHARED_DIR:-$TMPDIR/xchg},security_model=none,mount_tag=shared \
 | 
			
		||||
          ${if cfg.useBootLoader then ''
 | 
			
		||||
            ${mkDiskIfaceDriveFlag "0" "file=$NIX_DISK_IMAGE,cache=writeback,werror=report"} \
 | 
			
		||||
            ${mkDiskIfaceDriveFlag "1" "file=$TMPDIR/disk.img,media=disk"} \
 | 
			
		||||
            ${if cfg.useEFIBoot then ''
 | 
			
		||||
              -pflash $TMPDIR/bios.bin \
 | 
			
		||||
            '' else ''
 | 
			
		||||
            ''}
 | 
			
		||||
          '' else ''
 | 
			
		||||
            ${mkDiskIfaceDriveFlag "0" "file=$NIX_DISK_IMAGE,cache=writeback,werror=report"} \
 | 
			
		||||
            -kernel ${config.system.build.toplevel}/kernel \
 | 
			
		||||
            -initrd ${config.system.build.toplevel}/initrd \
 | 
			
		||||
            -append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.toplevel}/init regInfo=${regInfo}/registration ${consoles} $QEMU_KERNEL_PARAMS" \
 | 
			
		||||
          ''} \
 | 
			
		||||
          $extraDisks \
 | 
			
		||||
          ${qemuGraphics} \
 | 
			
		||||
          ${drivesCmdLine config.virtualisation.qemu.drives} \
 | 
			
		||||
          ${toString config.virtualisation.qemu.options} \
 | 
			
		||||
          $QEMU_OPTS \
 | 
			
		||||
          "$@"
 | 
			
		||||
@ -367,6 +383,12 @@ in
 | 
			
		||||
          '';
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
      drives =
 | 
			
		||||
        mkOption {
 | 
			
		||||
          type = types.listOf (types.submodule driveOpts);
 | 
			
		||||
          description = "Drives passed to qemu.";
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
      diskInterface =
 | 
			
		||||
        mkOption {
 | 
			
		||||
          default = "virtio";
 | 
			
		||||
@ -476,8 +498,49 @@ in
 | 
			
		||||
 | 
			
		||||
    # FIXME: Consolidate this one day.
 | 
			
		||||
    virtualisation.qemu.options = mkMerge [
 | 
			
		||||
      (mkIf (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [ "-vga std" "-usb" "-device usb-tablet,bus=usb-bus.0" ])
 | 
			
		||||
      (mkIf (pkgs.stdenv.isAarch32 || pkgs.stdenv.isAarch64) [ "-device virtio-gpu-pci" "-device usb-ehci,id=usb0" "-device usb-kbd" "-device usb-tablet" ])
 | 
			
		||||
      (mkIf (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [
 | 
			
		||||
        "-vga std" "-usb" "-device usb-tablet,bus=usb-bus.0"
 | 
			
		||||
      ])
 | 
			
		||||
      (mkIf (pkgs.stdenv.isAarch32 || pkgs.stdenv.isAarch64) [
 | 
			
		||||
        "-device virtio-gpu-pci" "-device usb-ehci,id=usb0" "-device usb-kbd" "-device usb-tablet"
 | 
			
		||||
      ])
 | 
			
		||||
      (mkIf (!cfg.useBootLoader) [
 | 
			
		||||
        "-kernel ${config.system.build.toplevel}/kernel"
 | 
			
		||||
        "-initrd ${config.system.build.toplevel}/initrd"
 | 
			
		||||
        ''-append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.toplevel}/init regInfo=${regInfo}/registration ${consoles} $QEMU_KERNEL_PARAMS"''
 | 
			
		||||
      ])
 | 
			
		||||
      (mkIf cfg.useEFIBoot [
 | 
			
		||||
        "-pflash $TMPDIR/bios.bin"
 | 
			
		||||
      ])
 | 
			
		||||
      (mkIf (!cfg.graphics) [
 | 
			
		||||
        "-nographic"
 | 
			
		||||
      ])
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    virtualisation.qemu.drives = mkMerge [
 | 
			
		||||
      (mkIf cfg.useBootLoader [
 | 
			
		||||
        {
 | 
			
		||||
          file = "$NIX_DISK_IMAGE";
 | 
			
		||||
          driveExtraOpts.cache = "writeback";
 | 
			
		||||
          driveExtraOpts.werror = "report";
 | 
			
		||||
        }
 | 
			
		||||
        {
 | 
			
		||||
          file = "$TMPDIR/disk.img";
 | 
			
		||||
          driveExtraOpts.media = "disk";
 | 
			
		||||
          deviceExtraOpts.bootindex = "1";
 | 
			
		||||
        }
 | 
			
		||||
      ])
 | 
			
		||||
      (mkIf (!cfg.useBootLoader) [
 | 
			
		||||
        {
 | 
			
		||||
          file = "$NIX_DISK_IMAGE";
 | 
			
		||||
          driveExtraOpts.cache = "writeback";
 | 
			
		||||
          driveExtraOpts.werror = "report";
 | 
			
		||||
        }
 | 
			
		||||
      ])
 | 
			
		||||
      (imap0 (idx: _: {
 | 
			
		||||
        file = "$(pwd)/empty${toString idx}.qcow2";
 | 
			
		||||
        driveExtraOpts.werror = "report";
 | 
			
		||||
      }) cfg.emptyDiskImages)
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    # Mount the host filesystem via 9P, and bind-mount the Nix store
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
let
 | 
			
		||||
  commonConfig = ./common/letsencrypt/common.nix;
 | 
			
		||||
in import ./make-test.nix {
 | 
			
		||||
in import ./make-test-python.nix {
 | 
			
		||||
  name = "acme";
 | 
			
		||||
 | 
			
		||||
  nodes = rec {
 | 
			
		||||
@ -90,39 +90,44 @@ in import ./make-test.nix {
 | 
			
		||||
      newServerSystem = nodes.webserver2.config.system.build.toplevel;
 | 
			
		||||
      switchToNewServer = "${newServerSystem}/bin/switch-to-configuration test";
 | 
			
		||||
    in
 | 
			
		||||
    # Note, waitForUnit does not work for oneshot services that do not have RemainAfterExit=true,
 | 
			
		||||
    # Note, wait_for_unit does not work for oneshot services that do not have RemainAfterExit=true,
 | 
			
		||||
    # this is because a oneshot goes from inactive => activating => inactive, and never
 | 
			
		||||
    # reaches the active state. To work around this, we create some mock target units which
 | 
			
		||||
    # get pulled in by the oneshot units. The target units linger after activation, and hence we
 | 
			
		||||
    # can use them to probe that a oneshot fired. It is a bit ugly, but it is the best we can do
 | 
			
		||||
    ''
 | 
			
		||||
      $client->start;
 | 
			
		||||
      $letsencrypt->start;
 | 
			
		||||
      $acmeStandalone->start;
 | 
			
		||||
      client.start()
 | 
			
		||||
      letsencrypt.start()
 | 
			
		||||
      acmeStandalone.start()
 | 
			
		||||
 | 
			
		||||
      $letsencrypt->waitForUnit("default.target");
 | 
			
		||||
      $letsencrypt->waitForUnit("pebble.service");
 | 
			
		||||
      letsencrypt.wait_for_unit("default.target")
 | 
			
		||||
      letsencrypt.wait_for_unit("pebble.service")
 | 
			
		||||
 | 
			
		||||
      subtest "can request certificate with HTTPS-01 challenge", sub {
 | 
			
		||||
        $acmeStandalone->waitForUnit("default.target");
 | 
			
		||||
        $acmeStandalone->succeed("systemctl start acme-standalone.com.service");
 | 
			
		||||
        $acmeStandalone->waitForUnit("acme-finished-standalone.com.target");
 | 
			
		||||
      };
 | 
			
		||||
      with subtest("can request certificate with HTTPS-01 challenge"):
 | 
			
		||||
          acmeStandalone.wait_for_unit("default.target")
 | 
			
		||||
          acmeStandalone.succeed("systemctl start acme-standalone.com.service")
 | 
			
		||||
          acmeStandalone.wait_for_unit("acme-finished-standalone.com.target")
 | 
			
		||||
 | 
			
		||||
      $client->waitForUnit("default.target");
 | 
			
		||||
      client.wait_for_unit("default.target")
 | 
			
		||||
 | 
			
		||||
      $client->succeed('curl https://acme-v02.api.letsencrypt.org:15000/roots/0 > /tmp/ca.crt');
 | 
			
		||||
      $client->succeed('curl https://acme-v02.api.letsencrypt.org:15000/intermediate-keys/0 >> /tmp/ca.crt');
 | 
			
		||||
      client.succeed("curl https://acme-v02.api.letsencrypt.org:15000/roots/0 > /tmp/ca.crt")
 | 
			
		||||
      client.succeed(
 | 
			
		||||
          "curl https://acme-v02.api.letsencrypt.org:15000/intermediate-keys/0 >> /tmp/ca.crt"
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      subtest "Can request certificate for nginx service", sub {
 | 
			
		||||
        $webserver->waitForUnit("acme-finished-a.example.com.target");
 | 
			
		||||
        $client->succeed('curl --cacert /tmp/ca.crt https://a.example.com/ | grep -qF "hello world"');
 | 
			
		||||
      };
 | 
			
		||||
      with subtest("Can request certificate for nginx service"):
 | 
			
		||||
          webserver.wait_for_unit("acme-finished-a.example.com.target")
 | 
			
		||||
          client.succeed(
 | 
			
		||||
              "curl --cacert /tmp/ca.crt https://a.example.com/ | grep -qF 'hello world'"
 | 
			
		||||
          )
 | 
			
		||||
 | 
			
		||||
      subtest "Can add another certificate for nginx service", sub {
 | 
			
		||||
        $webserver->succeed("/run/current-system/fine-tune/child-1/bin/switch-to-configuration test");
 | 
			
		||||
        $webserver->waitForUnit("acme-finished-b.example.com.target");
 | 
			
		||||
        $client->succeed('curl --cacert /tmp/ca.crt https://b.example.com/ | grep -qF "hello world"');
 | 
			
		||||
      };
 | 
			
		||||
      with subtest("Can add another certificate for nginx service"):
 | 
			
		||||
          webserver.succeed(
 | 
			
		||||
              "/run/current-system/fine-tune/child-1/bin/switch-to-configuration test"
 | 
			
		||||
          )
 | 
			
		||||
          webserver.wait_for_unit("acme-finished-b.example.com.target")
 | 
			
		||||
          client.succeed(
 | 
			
		||||
              "curl --cacert /tmp/ca.crt https://b.example.com/ | grep -qF 'hello world'"
 | 
			
		||||
          )
 | 
			
		||||
    '';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -170,6 +170,7 @@ in
 | 
			
		||||
  minio = handleTest ./minio.nix {};
 | 
			
		||||
  minidlna = handleTest ./minidlna.nix {};
 | 
			
		||||
  misc = handleTest ./misc.nix {};
 | 
			
		||||
  moinmoin = handleTest ./moinmoin.nix {};
 | 
			
		||||
  mongodb = handleTest ./mongodb.nix {};
 | 
			
		||||
  moodle = handleTest ./moodle.nix {};
 | 
			
		||||
  morty = handleTest ./morty.nix {};
 | 
			
		||||
@ -280,6 +281,7 @@ in
 | 
			
		||||
  tinydns = handleTest ./tinydns.nix {};
 | 
			
		||||
  tor = handleTest ./tor.nix {};
 | 
			
		||||
  transmission = handleTest ./transmission.nix {};
 | 
			
		||||
  trac = handleTest ./trac.nix {};
 | 
			
		||||
  trezord = handleTest ./trezord.nix {};
 | 
			
		||||
  trickster = handleTest ./trickster.nix {};
 | 
			
		||||
  udisks2 = handleTest ./udisks2.nix {};
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import ./make-test.nix ({ pkgs, ...} : {
 | 
			
		||||
import ./make-test-python.nix ({ pkgs, ...} : {
 | 
			
		||||
  name = "ammonite";
 | 
			
		||||
  meta = with pkgs.stdenv.lib.maintainers; {
 | 
			
		||||
    maintainers = [ nequissimus ];
 | 
			
		||||
@ -13,8 +13,8 @@ import ./make-test.nix ({ pkgs, ...} : {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  testScript = ''
 | 
			
		||||
    startAll;
 | 
			
		||||
    start_all()
 | 
			
		||||
 | 
			
		||||
    $amm->succeed("amm -c 'val foo = 21; println(foo * 2)' | grep 42")
 | 
			
		||||
    amm.succeed("amm -c 'val foo = 21; println(foo * 2)' | grep 42")
 | 
			
		||||
  '';
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import ./make-test.nix ({ pkgs, lib, ... }:
 | 
			
		||||
import ./make-test-python.nix ({ pkgs, lib, ... }:
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
  name = "automysqlbackup";
 | 
			
		||||
@ -15,20 +15,24 @@ import ./make-test.nix ({ pkgs, lib, ... }:
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  testScript = ''
 | 
			
		||||
    startAll;
 | 
			
		||||
    start_all()
 | 
			
		||||
 | 
			
		||||
    # Need to have mysql started so that it can be populated with data.
 | 
			
		||||
    $machine->waitForUnit("mysql.service");
 | 
			
		||||
    machine.wait_for_unit("mysql.service")
 | 
			
		||||
 | 
			
		||||
    # Wait for testdb to be fully populated (5 rows).
 | 
			
		||||
    $machine->waitUntilSucceeds("mysql -u root -D testdb -N -B -e 'select count(id) from tests' | grep -q 5");
 | 
			
		||||
    with subtest("Wait for testdb to be fully populated (5 rows)."):
 | 
			
		||||
        machine.wait_until_succeeds(
 | 
			
		||||
            "mysql -u root -D testdb -N -B -e 'select count(id) from tests' | grep -q 5"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    # Do a backup and wait for it to start
 | 
			
		||||
    $machine->startJob("automysqlbackup.service");
 | 
			
		||||
    $machine->waitForJob("automysqlbackup.service");
 | 
			
		||||
    with subtest("Do a backup and wait for it to start"):
 | 
			
		||||
        machine.start_job("automysqlbackup.service")
 | 
			
		||||
        machine.wait_for_job("automysqlbackup.service")
 | 
			
		||||
 | 
			
		||||
    # wait for backup file and check that data appears in backup
 | 
			
		||||
    $machine->waitForFile("/var/backup/mysql/daily/testdb");
 | 
			
		||||
    $machine->succeed("${pkgs.gzip}/bin/zcat /var/backup/mysql/daily/testdb/daily_testdb_*.sql.gz | grep hello");
 | 
			
		||||
    with subtest("wait for backup file and check that data appears in backup"):
 | 
			
		||||
        machine.wait_for_file("/var/backup/mysql/daily/testdb")
 | 
			
		||||
        machine.succeed(
 | 
			
		||||
            "${pkgs.gzip}/bin/zcat /var/backup/mysql/daily/testdb/daily_testdb_*.sql.gz | grep hello"
 | 
			
		||||
        )
 | 
			
		||||
    '';
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@
 | 
			
		||||
# which only works if the first client successfully uses the UPnP-IGD
 | 
			
		||||
# protocol to poke a hole in the NAT.
 | 
			
		||||
 | 
			
		||||
import ./make-test.nix ({ pkgs, ... }:
 | 
			
		||||
import ./make-test-python.nix ({ pkgs, ... }:
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
 | 
			
		||||
@ -108,42 +108,56 @@ in
 | 
			
		||||
  testScript =
 | 
			
		||||
    { nodes, ... }:
 | 
			
		||||
    ''
 | 
			
		||||
      startAll;
 | 
			
		||||
      start_all()
 | 
			
		||||
 | 
			
		||||
      # Wait for network and miniupnpd.
 | 
			
		||||
      $router->waitForUnit("network-online.target");
 | 
			
		||||
      $router->waitForUnit("miniupnpd");
 | 
			
		||||
      router.wait_for_unit("network-online.target")
 | 
			
		||||
      router.wait_for_unit("miniupnpd")
 | 
			
		||||
 | 
			
		||||
      # Create the torrent.
 | 
			
		||||
      $tracker->succeed("mkdir /tmp/data");
 | 
			
		||||
      $tracker->succeed("cp ${file} /tmp/data/test.tar.bz2");
 | 
			
		||||
      $tracker->succeed("transmission-create /tmp/data/test.tar.bz2 --private --tracker http://${externalTrackerAddress}:6969/announce --outfile /tmp/test.torrent");
 | 
			
		||||
      $tracker->succeed("chmod 644 /tmp/test.torrent");
 | 
			
		||||
      tracker.succeed("mkdir /tmp/data")
 | 
			
		||||
      tracker.succeed(
 | 
			
		||||
          "cp ${file} /tmp/data/test.tar.bz2"
 | 
			
		||||
      )
 | 
			
		||||
      tracker.succeed(
 | 
			
		||||
          "transmission-create /tmp/data/test.tar.bz2 --private --tracker http://${externalTrackerAddress}:6969/announce --outfile /tmp/test.torrent"
 | 
			
		||||
      )
 | 
			
		||||
      tracker.succeed("chmod 644 /tmp/test.torrent")
 | 
			
		||||
 | 
			
		||||
      # Start the tracker.  !!! use a less crappy tracker
 | 
			
		||||
      $tracker->waitForUnit("network-online.target");
 | 
			
		||||
      $tracker->waitForUnit("opentracker.service");
 | 
			
		||||
      $tracker->waitForOpenPort(6969);
 | 
			
		||||
      tracker.wait_for_unit("network-online.target")
 | 
			
		||||
      tracker.wait_for_unit("opentracker.service")
 | 
			
		||||
      tracker.wait_for_open_port(6969)
 | 
			
		||||
 | 
			
		||||
      # Start the initial seeder.
 | 
			
		||||
      $tracker->succeed("transmission-remote --add /tmp/test.torrent --no-portmap --no-dht --download-dir /tmp/data");
 | 
			
		||||
      tracker.succeed(
 | 
			
		||||
          "transmission-remote --add /tmp/test.torrent --no-portmap --no-dht --download-dir /tmp/data"
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      # Now we should be able to download from the client behind the NAT.
 | 
			
		||||
      $tracker->waitForUnit("httpd");
 | 
			
		||||
      $client1->waitForUnit("network-online.target");
 | 
			
		||||
      $client1->succeed("transmission-remote --add http://${externalTrackerAddress}/test.torrent --download-dir /tmp >&2 &");
 | 
			
		||||
      $client1->waitForFile("/tmp/test.tar.bz2");
 | 
			
		||||
      $client1->succeed("cmp /tmp/test.tar.bz2 ${file}");
 | 
			
		||||
      tracker.wait_for_unit("httpd")
 | 
			
		||||
      client1.wait_for_unit("network-online.target")
 | 
			
		||||
      client1.succeed(
 | 
			
		||||
          "transmission-remote --add http://${externalTrackerAddress}/test.torrent --download-dir /tmp >&2 &"
 | 
			
		||||
      )
 | 
			
		||||
      client1.wait_for_file("/tmp/test.tar.bz2")
 | 
			
		||||
      client1.succeed(
 | 
			
		||||
          "cmp /tmp/test.tar.bz2 ${file}"
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      # Bring down the initial seeder.
 | 
			
		||||
      # $tracker->stopJob("transmission");
 | 
			
		||||
      # tracker.stop_job("transmission")
 | 
			
		||||
 | 
			
		||||
      # Now download from the second client.  This can only succeed if
 | 
			
		||||
      # the first client created a NAT hole in the router.
 | 
			
		||||
      $client2->waitForUnit("network-online.target");
 | 
			
		||||
      $client2->succeed("transmission-remote --add http://${externalTrackerAddress}/test.torrent --no-portmap --no-dht --download-dir /tmp >&2 &");
 | 
			
		||||
      $client2->waitForFile("/tmp/test.tar.bz2");
 | 
			
		||||
      $client2->succeed("cmp /tmp/test.tar.bz2 ${file}");
 | 
			
		||||
      client2.wait_for_unit("network-online.target")
 | 
			
		||||
      client2.succeed(
 | 
			
		||||
          "transmission-remote --add http://${externalTrackerAddress}/test.torrent --no-portmap --no-dht --download-dir /tmp >&2 &"
 | 
			
		||||
      )
 | 
			
		||||
      client2.wait_for_file("/tmp/test.tar.bz2")
 | 
			
		||||
      client2.succeed(
 | 
			
		||||
          "cmp /tmp/test.tar.bz2 ${file}"
 | 
			
		||||
      )
 | 
			
		||||
    '';
 | 
			
		||||
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@
 | 
			
		||||
  pkgs ? import ../.. { inherit system config; }
 | 
			
		||||
}:
 | 
			
		||||
 | 
			
		||||
with import ../lib/testing.nix { inherit system pkgs; };
 | 
			
		||||
with import ../lib/testing-python.nix { inherit system pkgs; };
 | 
			
		||||
with pkgs.lib;
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
@ -17,11 +17,11 @@ let
 | 
			
		||||
        ];
 | 
			
		||||
    }).config.system.build.isoImage;
 | 
			
		||||
 | 
			
		||||
  perlAttrs = params: "{ ${concatStringsSep ", " (mapAttrsToList (name: param: "${name} => ${builtins.toJSON param}") params)} }";
 | 
			
		||||
  pythonDict = params: "\n    {\n        ${concatStringsSep ",\n        " (mapAttrsToList (name: param: "\"${name}\": \"${param}\"") params)},\n    }\n";
 | 
			
		||||
 | 
			
		||||
  makeBootTest = name: extraConfig:
 | 
			
		||||
    let
 | 
			
		||||
      machineConfig = perlAttrs ({ qemuFlags = "-m 768"; } // extraConfig);
 | 
			
		||||
      machineConfig = pythonDict ({ qemuFlags = "-m 768"; } // extraConfig);
 | 
			
		||||
    in
 | 
			
		||||
      makeTest {
 | 
			
		||||
        inherit iso;
 | 
			
		||||
@ -29,16 +29,16 @@ let
 | 
			
		||||
        nodes = { };
 | 
			
		||||
        testScript =
 | 
			
		||||
          ''
 | 
			
		||||
            my $machine = createMachine(${machineConfig});
 | 
			
		||||
            $machine->start;
 | 
			
		||||
            $machine->waitForUnit("multi-user.target");
 | 
			
		||||
            $machine->succeed("nix verify -r --no-trust /run/current-system");
 | 
			
		||||
            machine = create_machine(${machineConfig})
 | 
			
		||||
            machine.start()
 | 
			
		||||
            machine.wait_for_unit("multi-user.target")
 | 
			
		||||
            machine.succeed("nix verify -r --no-trust /run/current-system")
 | 
			
		||||
 | 
			
		||||
            # Test whether the channel got installed correctly.
 | 
			
		||||
            $machine->succeed("nix-instantiate --dry-run '<nixpkgs>' -A hello");
 | 
			
		||||
            $machine->succeed("nix-env --dry-run -iA nixos.procps");
 | 
			
		||||
            with subtest("Check whether the channel got installed correctly"):
 | 
			
		||||
                machine.succeed("nix-instantiate --dry-run '<nixpkgs>' -A hello")
 | 
			
		||||
                machine.succeed("nix-env --dry-run -iA nixos.procps")
 | 
			
		||||
 | 
			
		||||
            $machine->shutdown;
 | 
			
		||||
            machine.shutdown()
 | 
			
		||||
          '';
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
@ -60,7 +60,7 @@ let
 | 
			
		||||
          config.system.build.netbootIpxeScript
 | 
			
		||||
        ];
 | 
			
		||||
      };
 | 
			
		||||
      machineConfig = perlAttrs ({
 | 
			
		||||
      machineConfig = pythonDict ({
 | 
			
		||||
        qemuFlags = "-boot order=n -m 2000";
 | 
			
		||||
        netBackendArgs = "tftp=${ipxeBootDir},bootfile=netboot.ipxe";
 | 
			
		||||
      } // extraConfig);
 | 
			
		||||
@ -68,12 +68,11 @@ let
 | 
			
		||||
      makeTest {
 | 
			
		||||
        name = "boot-netboot-" + name;
 | 
			
		||||
        nodes = { };
 | 
			
		||||
        testScript =
 | 
			
		||||
          ''
 | 
			
		||||
            my $machine = createMachine(${machineConfig});
 | 
			
		||||
            $machine->start;
 | 
			
		||||
            $machine->waitForUnit("multi-user.target");
 | 
			
		||||
            $machine->shutdown;
 | 
			
		||||
        testScript = ''
 | 
			
		||||
            machine = create_machine(${machineConfig})
 | 
			
		||||
            machine.start()
 | 
			
		||||
            machine.wait_for_unit("multi-user.target")
 | 
			
		||||
            machine.shutdown()
 | 
			
		||||
          '';
 | 
			
		||||
      };
 | 
			
		||||
in {
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import ./make-test.nix ({ pkgs, ...} : {
 | 
			
		||||
import ./make-test-python.nix ({ pkgs, ...} : {
 | 
			
		||||
  name = "emacs-daemon";
 | 
			
		||||
  meta = with pkgs.stdenv.lib.maintainers; {
 | 
			
		||||
    maintainers = [ ];
 | 
			
		||||
@ -21,25 +21,28 @@ import ./make-test.nix ({ pkgs, ...} : {
 | 
			
		||||
      environment.variables.TEST_SYSTEM_VARIABLE = "system variable";
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  testScript =
 | 
			
		||||
    ''
 | 
			
		||||
      $machine->waitForUnit("multi-user.target");
 | 
			
		||||
  testScript = ''
 | 
			
		||||
      machine.wait_for_unit("multi-user.target")
 | 
			
		||||
 | 
			
		||||
      # checks that the EDITOR environment variable is set
 | 
			
		||||
      $machine->succeed("test \$(basename \"\$EDITOR\") = emacseditor");
 | 
			
		||||
      machine.succeed('test $(basename "$EDITOR") = emacseditor')
 | 
			
		||||
 | 
			
		||||
      # waits for the emacs service to be ready
 | 
			
		||||
      $machine->waitUntilSucceeds("systemctl --user status emacs.service | grep 'Active: active'");
 | 
			
		||||
      machine.wait_until_succeeds(
 | 
			
		||||
          "systemctl --user status emacs.service | grep 'Active: active'"
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      # connects to the daemon
 | 
			
		||||
      $machine->succeed("emacsclient --create-frame \$EDITOR &");
 | 
			
		||||
      machine.succeed("emacsclient --create-frame $EDITOR &")
 | 
			
		||||
 | 
			
		||||
      # checks that Emacs shows the edited filename
 | 
			
		||||
      $machine->waitForText("emacseditor");
 | 
			
		||||
      machine.wait_for_text("emacseditor")
 | 
			
		||||
 | 
			
		||||
      # makes sure environment variables are accessible from Emacs
 | 
			
		||||
      $machine->succeed("emacsclient --eval '(getenv \"TEST_SYSTEM_VARIABLE\")'") =~ /system variable/ or die;
 | 
			
		||||
      machine.succeed(
 | 
			
		||||
          "emacsclient --eval '(getenv \"TEST_SYSTEM_VARIABLE\")' | grep -q 'system variable'"
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      $machine->screenshot("emacsclient");
 | 
			
		||||
      machine.screenshot("emacsclient")
 | 
			
		||||
    '';
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import ./make-test.nix {
 | 
			
		||||
import ./make-test-python.nix {
 | 
			
		||||
  name = "fsck";
 | 
			
		||||
 | 
			
		||||
  machine = { lib, ... }: {
 | 
			
		||||
@ -14,16 +14,18 @@ import ./make-test.nix {
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  testScript = ''
 | 
			
		||||
    $machine->waitForUnit('default.target');
 | 
			
		||||
    machine.wait_for_unit("default.target")
 | 
			
		||||
 | 
			
		||||
    subtest "root fs is fsckd", sub {
 | 
			
		||||
      $machine->succeed('journalctl -b | grep "fsck.ext4.*/dev/vda"');
 | 
			
		||||
    };
 | 
			
		||||
    with subtest("root fs is fsckd"):
 | 
			
		||||
        machine.succeed("journalctl -b | grep 'fsck.ext4.*/dev/vda'")
 | 
			
		||||
 | 
			
		||||
    subtest "mnt fs is fsckd", sub {
 | 
			
		||||
      $machine->succeed('journalctl -b | grep "fsck.*/dev/vdb.*clean"');
 | 
			
		||||
      $machine->succeed('grep "Requires=systemd-fsck@dev-vdb.service" /run/systemd/generator/mnt.mount');
 | 
			
		||||
      $machine->succeed('grep "After=systemd-fsck@dev-vdb.service" /run/systemd/generator/mnt.mount');
 | 
			
		||||
    };
 | 
			
		||||
    with subtest("mnt fs is fsckd"):
 | 
			
		||||
        machine.succeed("journalctl -b | grep 'fsck.*/dev/vdb.*clean'")
 | 
			
		||||
        machine.succeed(
 | 
			
		||||
            "grep 'Requires=systemd-fsck@dev-vdb.service' /run/systemd/generator/mnt.mount"
 | 
			
		||||
        )
 | 
			
		||||
        machine.succeed(
 | 
			
		||||
            "grep 'After=systemd-fsck@dev-vdb.service' /run/systemd/generator/mnt.mount"
 | 
			
		||||
        )
 | 
			
		||||
  '';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@
 | 
			
		||||
  pkgs ? import ../.. { inherit system config; }
 | 
			
		||||
}:
 | 
			
		||||
 | 
			
		||||
with import ../lib/testing.nix { inherit system pkgs; };
 | 
			
		||||
with import ../lib/testing-python.nix { inherit system pkgs; };
 | 
			
		||||
with pkgs.lib;
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
@ -18,11 +18,11 @@ with pkgs.lib;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    testScript = ''
 | 
			
		||||
      startAll;
 | 
			
		||||
      start_all()
 | 
			
		||||
 | 
			
		||||
      $machine->waitForUnit('gitea.service');
 | 
			
		||||
      $machine->waitForOpenPort('3000');
 | 
			
		||||
      $machine->succeed("curl --fail http://localhost:3000/");
 | 
			
		||||
      machine.wait_for_unit("gitea.service")
 | 
			
		||||
      machine.wait_for_open_port(3000)
 | 
			
		||||
      machine.succeed("curl --fail http://localhost:3000/")
 | 
			
		||||
    '';
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@ -37,11 +37,11 @@ with pkgs.lib;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    testScript = ''
 | 
			
		||||
      startAll;
 | 
			
		||||
      start_all()
 | 
			
		||||
 | 
			
		||||
      $machine->waitForUnit('gitea.service');
 | 
			
		||||
      $machine->waitForOpenPort('3000');
 | 
			
		||||
      $machine->succeed("curl --fail http://localhost:3000/");
 | 
			
		||||
      machine.wait_for_unit("gitea.service")
 | 
			
		||||
      machine.wait_for_open_port(3000)
 | 
			
		||||
      machine.succeed("curl --fail http://localhost:3000/")
 | 
			
		||||
    '';
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@ -56,12 +56,14 @@ with pkgs.lib;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    testScript = ''
 | 
			
		||||
      startAll;
 | 
			
		||||
      start_all()
 | 
			
		||||
 | 
			
		||||
      $machine->waitForUnit('gitea.service');
 | 
			
		||||
      $machine->waitForOpenPort('3000');
 | 
			
		||||
      $machine->succeed("curl --fail http://localhost:3000/");
 | 
			
		||||
      $machine->succeed("curl --fail http://localhost:3000/user/sign_up | grep 'Registration is disabled. Please contact your site administrator.'");
 | 
			
		||||
      machine.wait_for_unit("gitea.service")
 | 
			
		||||
      machine.wait_for_open_port(3000)
 | 
			
		||||
      machine.succeed("curl --fail http://localhost:3000/")
 | 
			
		||||
      machine.succeed(
 | 
			
		||||
          "curl --fail http://localhost:3000/user/sign_up | grep 'Registration is disabled. Please contact your site administrator.'"
 | 
			
		||||
      )
 | 
			
		||||
    '';
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import ./make-test.nix ({ pkgs, latestKernel ? false, ... }:
 | 
			
		||||
import ./make-test-python.nix ({ pkgs, latestKernel ? false, ... }:
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
  name = "login";
 | 
			
		||||
@ -12,62 +12,48 @@ import ./make-test.nix ({ pkgs, latestKernel ? false, ... }:
 | 
			
		||||
      sound.enable = true; # needed for the factl test, /dev/snd/* exists without them but udev doesn't care then
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  testScript =
 | 
			
		||||
    ''
 | 
			
		||||
      $machine->waitForUnit('multi-user.target');
 | 
			
		||||
      $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty1'");
 | 
			
		||||
      $machine->screenshot("postboot");
 | 
			
		||||
  testScript = ''
 | 
			
		||||
      machine.wait_for_unit("multi-user.target")
 | 
			
		||||
      machine.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
 | 
			
		||||
      machine.screenshot("postboot")
 | 
			
		||||
 | 
			
		||||
      subtest "create user", sub {
 | 
			
		||||
          $machine->succeed("useradd -m alice");
 | 
			
		||||
          $machine->succeed("(echo foobar; echo foobar) | passwd alice");
 | 
			
		||||
      };
 | 
			
		||||
      with subtest("create user"):
 | 
			
		||||
          machine.succeed("useradd -m alice")
 | 
			
		||||
          machine.succeed("(echo foobar; echo foobar) | passwd alice")
 | 
			
		||||
 | 
			
		||||
      # Check whether switching VTs works.
 | 
			
		||||
      subtest "virtual console switching", sub {
 | 
			
		||||
          $machine->fail("pgrep -f 'agetty.*tty2'");
 | 
			
		||||
          $machine->sendKeys("alt-f2");
 | 
			
		||||
          $machine->waitUntilSucceeds("[ \$(fgconsole) = 2 ]");
 | 
			
		||||
          $machine->waitForUnit('getty@tty2.service');
 | 
			
		||||
          $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty2'");
 | 
			
		||||
      };
 | 
			
		||||
      with subtest("Check whether switching VTs works"):
 | 
			
		||||
          machine.fail("pgrep -f 'agetty.*tty2'")
 | 
			
		||||
          machine.send_key("alt-f2")
 | 
			
		||||
          machine.wait_until_succeeds("[ $(fgconsole) = 2 ]")
 | 
			
		||||
          machine.wait_for_unit("getty@tty2.service")
 | 
			
		||||
          machine.wait_until_succeeds("pgrep -f 'agetty.*tty2'")
 | 
			
		||||
 | 
			
		||||
      # Log in as alice on a virtual console.
 | 
			
		||||
      subtest "virtual console login", sub {
 | 
			
		||||
          $machine->waitUntilTTYMatches(2, "login: ");
 | 
			
		||||
          $machine->sendChars("alice\n");
 | 
			
		||||
          $machine->waitUntilTTYMatches(2, "login: alice");
 | 
			
		||||
          $machine->waitUntilSucceeds("pgrep login");
 | 
			
		||||
          $machine->waitUntilTTYMatches(2, "Password: ");
 | 
			
		||||
          $machine->sendChars("foobar\n");
 | 
			
		||||
          $machine->waitUntilSucceeds("pgrep -u alice bash");
 | 
			
		||||
          $machine->sendChars("touch done\n");
 | 
			
		||||
          $machine->waitForFile("/home/alice/done");
 | 
			
		||||
      };
 | 
			
		||||
      with subtest("Log in as alice on a virtual console"):
 | 
			
		||||
          machine.wait_until_tty_matches(2, "login: ")
 | 
			
		||||
          machine.send_chars("alice\n")
 | 
			
		||||
          machine.wait_until_tty_matches(2, "login: alice")
 | 
			
		||||
          machine.wait_until_succeeds("pgrep login")
 | 
			
		||||
          machine.wait_until_tty_matches(2, "Password: ")
 | 
			
		||||
          machine.send_chars("foobar\n")
 | 
			
		||||
          machine.wait_until_succeeds("pgrep -u alice bash")
 | 
			
		||||
          machine.send_chars("touch done\n")
 | 
			
		||||
          machine.wait_for_file("/home/alice/done")
 | 
			
		||||
 | 
			
		||||
      # Check whether systemd gives and removes device ownership as
 | 
			
		||||
      # needed.
 | 
			
		||||
      subtest "device permissions", sub {
 | 
			
		||||
          $machine->succeed("getfacl -p /dev/snd/timer | grep -q alice");
 | 
			
		||||
          $machine->sendKeys("alt-f1");
 | 
			
		||||
          $machine->waitUntilSucceeds("[ \$(fgconsole) = 1 ]");
 | 
			
		||||
          $machine->fail("getfacl -p /dev/snd/timer | grep -q alice");
 | 
			
		||||
          $machine->succeed("chvt 2");
 | 
			
		||||
          $machine->waitUntilSucceeds("getfacl -p /dev/snd/timer | grep -q alice");
 | 
			
		||||
      };
 | 
			
		||||
      with subtest("Systemd gives and removes device ownership as needed"):
 | 
			
		||||
          machine.succeed("getfacl /dev/snd/timer | grep -q alice")
 | 
			
		||||
          machine.send_key("alt-f1")
 | 
			
		||||
          machine.wait_until_succeeds("[ $(fgconsole) = 1 ]")
 | 
			
		||||
          machine.fail("getfacl /dev/snd/timer | grep -q alice")
 | 
			
		||||
          machine.succeed("chvt 2")
 | 
			
		||||
          machine.wait_until_succeeds("getfacl /dev/snd/timer | grep -q alice")
 | 
			
		||||
 | 
			
		||||
      # Log out.
 | 
			
		||||
      subtest "virtual console logout", sub {
 | 
			
		||||
          $machine->sendChars("exit\n");
 | 
			
		||||
          $machine->waitUntilFails("pgrep -u alice bash");
 | 
			
		||||
          $machine->screenshot("mingetty");
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      # Check whether ctrl-alt-delete works.
 | 
			
		||||
      subtest "ctrl-alt-delete", sub {
 | 
			
		||||
          $machine->sendKeys("ctrl-alt-delete");
 | 
			
		||||
          $machine->waitForShutdown;
 | 
			
		||||
      };
 | 
			
		||||
    '';
 | 
			
		||||
      with subtest("Virtual console logout"):
 | 
			
		||||
          machine.send_chars("exit\n")
 | 
			
		||||
          machine.wait_until_fails("pgrep -u alice bash")
 | 
			
		||||
          machine.screenshot("mingetty")
 | 
			
		||||
 | 
			
		||||
      with subtest("Check whether ctrl-alt-delete works"):
 | 
			
		||||
          machine.send_key("ctrl-alt-delete")
 | 
			
		||||
          machine.wait_for_shutdown()
 | 
			
		||||
  '';
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								nixos/tests/make-test-python.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								nixos/tests/make-test-python.nix
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
f: {
 | 
			
		||||
  system ? builtins.currentSystem,
 | 
			
		||||
  pkgs ? import ../.. { inherit system; config = {}; },
 | 
			
		||||
  ...
 | 
			
		||||
} @ args:
 | 
			
		||||
 | 
			
		||||
with import ../lib/testing-python.nix { inherit system pkgs; };
 | 
			
		||||
 | 
			
		||||
makeTest (if pkgs.lib.isFunction f then f (args // { inherit pkgs; inherit (pkgs) lib; }) else f)
 | 
			
		||||
							
								
								
									
										24
									
								
								nixos/tests/moinmoin.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								nixos/tests/moinmoin.nix
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
import ./make-test.nix ({ pkgs, lib, ... }: {
 | 
			
		||||
  name = "moinmoin";
 | 
			
		||||
  meta.maintainers = [ ]; # waiting for https://github.com/NixOS/nixpkgs/pull/65397
 | 
			
		||||
 | 
			
		||||
  machine =
 | 
			
		||||
    { ... }:
 | 
			
		||||
    { services.moinmoin.enable = true;
 | 
			
		||||
      services.moinmoin.wikis.ExampleWiki.superUsers = [ "admin" ];
 | 
			
		||||
      services.moinmoin.wikis.ExampleWiki.webHost = "localhost";
 | 
			
		||||
 | 
			
		||||
      services.nginx.virtualHosts.localhost.enableACME = false;
 | 
			
		||||
      services.nginx.virtualHosts.localhost.forceSSL = false;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  testScript = ''
 | 
			
		||||
    startAll;
 | 
			
		||||
 | 
			
		||||
    $machine->waitForUnit('moin-ExampleWiki.service');
 | 
			
		||||
    $machine->waitForUnit('nginx.service');
 | 
			
		||||
    $machine->waitForFile('/run/moin/ExampleWiki/gunicorn.sock');
 | 
			
		||||
    $machine->succeed('curl -L http://localhost/') =~ /If you have just installed/ or die;
 | 
			
		||||
    $machine->succeed('moin-ExampleWiki account create --name=admin --email=admin@example.com --password=foo 2>&1') =~ /status success/ or die;
 | 
			
		||||
  '';
 | 
			
		||||
})
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import ./make-test.nix ({ lib, ... } : {
 | 
			
		||||
import ./make-test-python.nix ({ lib, ... } : {
 | 
			
		||||
  name = "nixos-generate-config";
 | 
			
		||||
  meta.maintainers = with lib.maintainers; [ basvandijk ];
 | 
			
		||||
  machine = {
 | 
			
		||||
@ -11,14 +11,16 @@ import ./make-test.nix ({ lib, ... } : {
 | 
			
		||||
    '';
 | 
			
		||||
  };
 | 
			
		||||
  testScript = ''
 | 
			
		||||
    startAll;
 | 
			
		||||
    $machine->waitForUnit("multi-user.target");
 | 
			
		||||
    $machine->succeed("nixos-generate-config");
 | 
			
		||||
    start_all()
 | 
			
		||||
    machine.wait_for_unit("multi-user.target")
 | 
			
		||||
    machine.succeed("nixos-generate-config")
 | 
			
		||||
 | 
			
		||||
    # Test if the configuration really is overridden
 | 
			
		||||
    $machine->succeed("grep 'OVERRIDDEN' /etc/nixos/configuration.nix");
 | 
			
		||||
    machine.succeed("grep 'OVERRIDDEN' /etc/nixos/configuration.nix")
 | 
			
		||||
 | 
			
		||||
    # Test of if the Perl variable $bootLoaderConfig is spliced correctly:
 | 
			
		||||
    $machine->succeed("grep 'boot\\.loader\\.grub\\.enable = true;' /etc/nixos/configuration.nix");
 | 
			
		||||
    machine.succeed(
 | 
			
		||||
        "grep 'boot\\.loader\\.grub\\.enable = true;' /etc/nixos/configuration.nix"
 | 
			
		||||
    )
 | 
			
		||||
  '';
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import ./make-test.nix ({ pkgs, ... }:
 | 
			
		||||
import ./make-test-python.nix ({ pkgs, ... }:
 | 
			
		||||
 | 
			
		||||
let inherit (import ./ssh-keys.nix pkgs)
 | 
			
		||||
      snakeOilPrivateKey snakeOilPublicKey;
 | 
			
		||||
@ -58,47 +58,55 @@ in {
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  testScript = ''
 | 
			
		||||
    startAll;
 | 
			
		||||
    start_all()
 | 
			
		||||
 | 
			
		||||
    my $key=`${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f key -N ""`;
 | 
			
		||||
    server.wait_for_unit("sshd")
 | 
			
		||||
 | 
			
		||||
    $server->waitForUnit("sshd");
 | 
			
		||||
    with subtest("manual-authkey"):
 | 
			
		||||
        client.succeed("mkdir -m 700 /root/.ssh")
 | 
			
		||||
        client.succeed(
 | 
			
		||||
            '${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N ""'
 | 
			
		||||
        )
 | 
			
		||||
        public_key = client.succeed(
 | 
			
		||||
            "${pkgs.openssh}/bin/ssh-keygen -y -f /root/.ssh/id_ed25519"
 | 
			
		||||
        )
 | 
			
		||||
        public_key = public_key.strip()
 | 
			
		||||
        client.succeed("chmod 600 /root/.ssh/id_ed25519")
 | 
			
		||||
 | 
			
		||||
    subtest "manual-authkey", sub {
 | 
			
		||||
      $server->succeed("mkdir -m 700 /root/.ssh");
 | 
			
		||||
      $server->copyFileFromHost("key.pub", "/root/.ssh/authorized_keys");
 | 
			
		||||
      $server_lazy->succeed("mkdir -m 700 /root/.ssh");
 | 
			
		||||
      $server_lazy->copyFileFromHost("key.pub", "/root/.ssh/authorized_keys");
 | 
			
		||||
        server.succeed("mkdir -m 700 /root/.ssh")
 | 
			
		||||
        server.succeed("echo '{}' > /root/.ssh/authorized_keys".format(public_key))
 | 
			
		||||
        server_lazy.succeed("mkdir -m 700 /root/.ssh")
 | 
			
		||||
        server_lazy.succeed("echo '{}' > /root/.ssh/authorized_keys".format(public_key))
 | 
			
		||||
 | 
			
		||||
      $client->succeed("mkdir -m 700 /root/.ssh");
 | 
			
		||||
      $client->copyFileFromHost("key", "/root/.ssh/id_ed25519");
 | 
			
		||||
      $client->succeed("chmod 600 /root/.ssh/id_ed25519");
 | 
			
		||||
        client.wait_for_unit("network.target")
 | 
			
		||||
        client.succeed(
 | 
			
		||||
            "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'echo hello world' >&2"
 | 
			
		||||
        )
 | 
			
		||||
        client.succeed(
 | 
			
		||||
            "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'ulimit -l' | grep 1024"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
      $client->waitForUnit("network.target");
 | 
			
		||||
      $client->succeed("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'echo hello world' >&2");
 | 
			
		||||
      $client->succeed("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'ulimit -l' | grep 1024");
 | 
			
		||||
        client.succeed(
 | 
			
		||||
            "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server_lazy 'echo hello world' >&2"
 | 
			
		||||
        )
 | 
			
		||||
        client.succeed(
 | 
			
		||||
            "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server_lazy 'ulimit -l' | grep 1024"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
      $client->succeed("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server_lazy 'echo hello world' >&2");
 | 
			
		||||
      $client->succeed("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server_lazy 'ulimit -l' | grep 1024");
 | 
			
		||||
    with subtest("configured-authkey"):
 | 
			
		||||
        client.succeed(
 | 
			
		||||
            "cat ${snakeOilPrivateKey} > privkey.snakeoil"
 | 
			
		||||
        )
 | 
			
		||||
        client.succeed("chmod 600 privkey.snakeoil")
 | 
			
		||||
        client.succeed(
 | 
			
		||||
            "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server true"
 | 
			
		||||
        )
 | 
			
		||||
        client.succeed(
 | 
			
		||||
            "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server_lazy true"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    subtest "configured-authkey", sub {
 | 
			
		||||
      $client->succeed("cat ${snakeOilPrivateKey} > privkey.snakeoil");
 | 
			
		||||
      $client->succeed("chmod 600 privkey.snakeoil");
 | 
			
		||||
      $client->succeed("ssh -o UserKnownHostsFile=/dev/null" .
 | 
			
		||||
                       " -o StrictHostKeyChecking=no -i privkey.snakeoil" .
 | 
			
		||||
                       " server true");
 | 
			
		||||
 | 
			
		||||
      $client->succeed("ssh -o UserKnownHostsFile=/dev/null" .
 | 
			
		||||
                       " -o StrictHostKeyChecking=no -i privkey.snakeoil" .
 | 
			
		||||
                       " server_lazy true");
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    subtest "localhost-only", sub {
 | 
			
		||||
      $server_localhost_only->succeed("ss -nlt | grep '127.0.0.1:22'");
 | 
			
		||||
      $server_localhost_only_lazy->succeed("ss -nlt | grep '127.0.0.1:22'");
 | 
			
		||||
    }
 | 
			
		||||
    with subtest("localhost-only"):
 | 
			
		||||
        server_localhost_only.succeed("ss -nlt | grep '127.0.0.1:22'")
 | 
			
		||||
        server_localhost_only_lazy.succeed("ss -nlt | grep '127.0.0.1:22'")
 | 
			
		||||
  '';
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@
 | 
			
		||||
  pkgs ? import ../.. { inherit system config; }
 | 
			
		||||
}:
 | 
			
		||||
 | 
			
		||||
with import ../lib/testing.nix { inherit system pkgs; };
 | 
			
		||||
with import ../lib/testing-python.nix { inherit system pkgs; };
 | 
			
		||||
with pkgs.lib;
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
@ -40,29 +40,33 @@ let
 | 
			
		||||
      backupName = if backup-all then "all" else "postgres";
 | 
			
		||||
      backupService = if backup-all then "postgresqlBackup" else "postgresqlBackup-postgres";
 | 
			
		||||
    in ''
 | 
			
		||||
      sub check_count {
 | 
			
		||||
        my ($select, $nlines) = @_;
 | 
			
		||||
        return 'test $(sudo -u postgres psql postgres -tAc "' . $select . '"|wc -l) -eq ' . $nlines;
 | 
			
		||||
      }
 | 
			
		||||
      def check_count(statement, lines):
 | 
			
		||||
          return 'test $(sudo -u postgres psql postgres -tAc "{}"|wc -l) -eq {}'.format(
 | 
			
		||||
              statement, lines
 | 
			
		||||
          )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      machine.start()
 | 
			
		||||
      machine.wait_for_unit("postgresql")
 | 
			
		||||
 | 
			
		||||
      $machine->start;
 | 
			
		||||
      $machine->waitForUnit("postgresql");
 | 
			
		||||
      # postgresql should be available just after unit start
 | 
			
		||||
      $machine->succeed("cat ${test-sql} | sudo -u postgres psql");
 | 
			
		||||
      $machine->shutdown; # make sure that postgresql survive restart (bug #1735)
 | 
			
		||||
      sleep(2);
 | 
			
		||||
      $machine->start;
 | 
			
		||||
      $machine->waitForUnit("postgresql");
 | 
			
		||||
      $machine->fail(check_count("SELECT * FROM sth;", 3));
 | 
			
		||||
      $machine->succeed(check_count("SELECT * FROM sth;", 5));
 | 
			
		||||
      $machine->fail(check_count("SELECT * FROM sth;", 4));
 | 
			
		||||
      $machine->succeed(check_count("SELECT xpath(\'/test/text()\', doc) FROM xmltest;", 1));
 | 
			
		||||
      machine.succeed(
 | 
			
		||||
          "cat ${test-sql} | sudo -u postgres psql"
 | 
			
		||||
      )
 | 
			
		||||
      machine.shutdown()  # make sure that postgresql survive restart (bug #1735)
 | 
			
		||||
      time.sleep(2)
 | 
			
		||||
      machine.start()
 | 
			
		||||
      machine.wait_for_unit("postgresql")
 | 
			
		||||
      machine.fail(check_count("SELECT * FROM sth;", 3))
 | 
			
		||||
      machine.succeed(check_count("SELECT * FROM sth;", 5))
 | 
			
		||||
      machine.fail(check_count("SELECT * FROM sth;", 4))
 | 
			
		||||
      machine.succeed(check_count("SELECT xpath('/test/text()', doc) FROM xmltest;", 1))
 | 
			
		||||
 | 
			
		||||
      # Check backup service
 | 
			
		||||
      $machine->succeed("systemctl start ${backupService}.service");
 | 
			
		||||
      $machine->succeed("zcat /var/backup/postgresql/${backupName}.sql.gz | grep '<test>ok</test>'");
 | 
			
		||||
      $machine->succeed("stat -c '%a' /var/backup/postgresql/${backupName}.sql.gz | grep 600");
 | 
			
		||||
      $machine->shutdown;
 | 
			
		||||
      machine.succeed("systemctl start ${backupService}.service")
 | 
			
		||||
      machine.succeed("zcat /var/backup/postgresql/${backupName}.sql.gz | grep '<test>ok</test>'")
 | 
			
		||||
      machine.succeed("stat -c '%a' /var/backup/postgresql/${backupName}.sql.gz | grep 600")
 | 
			
		||||
      machine.shutdown()
 | 
			
		||||
    '';
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import ./make-test.nix ({ pkgs, ...} :
 | 
			
		||||
import ./make-test-python.nix ({ pkgs, ...} :
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
 | 
			
		||||
@ -59,37 +59,37 @@ rec {
 | 
			
		||||
 | 
			
		||||
  testScript =
 | 
			
		||||
    ''
 | 
			
		||||
      startAll;
 | 
			
		||||
      start_all()
 | 
			
		||||
 | 
			
		||||
      $server->waitForUnit("quake3-server");
 | 
			
		||||
      $client1->waitForX;
 | 
			
		||||
      $client2->waitForX;
 | 
			
		||||
      server.wait_for_unit("quake3-server")
 | 
			
		||||
      client1.wait_for_x()
 | 
			
		||||
      client2.wait_for_x()
 | 
			
		||||
 | 
			
		||||
      $client1->execute("quake3 +set r_fullscreen 0 +set name Foo +connect server &");
 | 
			
		||||
      $client2->execute("quake3 +set r_fullscreen 0 +set name Bar +connect server &");
 | 
			
		||||
      client1.execute("quake3 +set r_fullscreen 0 +set name Foo +connect server &")
 | 
			
		||||
      client2.execute("quake3 +set r_fullscreen 0 +set name Bar +connect server &")
 | 
			
		||||
 | 
			
		||||
      $server->waitUntilSucceeds("grep -q 'Foo.*entered the game' /tmp/log");
 | 
			
		||||
      $server->waitUntilSucceeds("grep -q 'Bar.*entered the game' /tmp/log");
 | 
			
		||||
      server.wait_until_succeeds("grep -q 'Foo.*entered the game' /tmp/log")
 | 
			
		||||
      server.wait_until_succeeds("grep -q 'Bar.*entered the game' /tmp/log")
 | 
			
		||||
 | 
			
		||||
      $server->sleep(10); # wait for a while to get a nice screenshot
 | 
			
		||||
      server.sleep(10)  # wait for a while to get a nice screenshot
 | 
			
		||||
 | 
			
		||||
      $client1->block();
 | 
			
		||||
      client1.block()
 | 
			
		||||
 | 
			
		||||
      $server->sleep(20);
 | 
			
		||||
      server.sleep(20)
 | 
			
		||||
 | 
			
		||||
      $client1->screenshot("screen1");
 | 
			
		||||
      $client2->screenshot("screen2");
 | 
			
		||||
      client1.screenshot("screen1")
 | 
			
		||||
      client2.screenshot("screen2")
 | 
			
		||||
 | 
			
		||||
      $client1->unblock();
 | 
			
		||||
      client1.unblock()
 | 
			
		||||
 | 
			
		||||
      $server->sleep(10);
 | 
			
		||||
      server.sleep(10)
 | 
			
		||||
 | 
			
		||||
      $client1->screenshot("screen3");
 | 
			
		||||
      $client2->screenshot("screen4");
 | 
			
		||||
      client1.screenshot("screen3")
 | 
			
		||||
      client2.screenshot("screen4")
 | 
			
		||||
 | 
			
		||||
      $client1->shutdown();
 | 
			
		||||
      $client2->shutdown();
 | 
			
		||||
      $server->stopJob("quake3-server");
 | 
			
		||||
      client1.shutdown()
 | 
			
		||||
      client2.shutdown()
 | 
			
		||||
      server.stop_job("quake3-server")
 | 
			
		||||
    '';
 | 
			
		||||
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import ./make-test.nix ({ pkgs, ...} : {
 | 
			
		||||
import ./make-test-python.nix ({ pkgs, ...} : {
 | 
			
		||||
  name = "simple";
 | 
			
		||||
  meta = with pkgs.stdenv.lib.maintainers; {
 | 
			
		||||
    maintainers = [ eelco ];
 | 
			
		||||
@ -10,8 +10,8 @@ import ./make-test.nix ({ pkgs, ...} : {
 | 
			
		||||
 | 
			
		||||
  testScript =
 | 
			
		||||
    ''
 | 
			
		||||
      startAll;
 | 
			
		||||
      $machine->waitForUnit("multi-user.target");
 | 
			
		||||
      $machine->shutdown;
 | 
			
		||||
      start_all()
 | 
			
		||||
      machine.wait_for_unit("multi-user.target")
 | 
			
		||||
      machine.shutdown()
 | 
			
		||||
    '';
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import ./make-test.nix ({ lib, ... }: with lib;
 | 
			
		||||
import ./make-test-python.nix ({ lib, ... }: with lib;
 | 
			
		||||
 | 
			
		||||
rec {
 | 
			
		||||
  name = "tor";
 | 
			
		||||
@ -21,8 +21,10 @@ rec {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  testScript = ''
 | 
			
		||||
    $client->waitForUnit("tor.service");
 | 
			
		||||
    $client->waitForOpenPort(9051);
 | 
			
		||||
    $client->succeed("echo GETINFO version | nc 127.0.0.1 9051") =~ /514 Authentication required./ or die;
 | 
			
		||||
    client.wait_for_unit("tor.service")
 | 
			
		||||
    client.wait_for_open_port(9051)
 | 
			
		||||
    assert "514 Authentication required." in client.succeed(
 | 
			
		||||
        "echo GETINFO version | nc 127.0.0.1 9051"
 | 
			
		||||
    )
 | 
			
		||||
  '';
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										19
									
								
								nixos/tests/trac.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								nixos/tests/trac.nix
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
import ./make-test.nix ({ pkgs, ... }: {
 | 
			
		||||
  name = "trac";
 | 
			
		||||
  meta = with pkgs.stdenv.lib.maintainers; {
 | 
			
		||||
    maintainers = [ mmahut ];
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  nodes = {
 | 
			
		||||
    machine = { ... }: {
 | 
			
		||||
      services.trac.enable = true;
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  testScript = ''
 | 
			
		||||
    startAll;
 | 
			
		||||
    $machine->waitForUnit("trac.service");
 | 
			
		||||
    $machine->waitForOpenPort(8000);
 | 
			
		||||
    $machine->waitUntilSucceeds("curl -L http://localhost:8000/ | grep 'Trac Powered'");
 | 
			
		||||
  '';
 | 
			
		||||
})
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import ./make-test.nix ({ pkgs, ...} : {
 | 
			
		||||
import ./make-test-python.nix ({ pkgs, ...} : {
 | 
			
		||||
  name = "transmission";
 | 
			
		||||
  meta = with pkgs.stdenv.lib.maintainers; {
 | 
			
		||||
    maintainers = [ coconnor ];
 | 
			
		||||
@ -14,8 +14,8 @@ import ./make-test.nix ({ pkgs, ...} : {
 | 
			
		||||
 | 
			
		||||
  testScript =
 | 
			
		||||
    ''
 | 
			
		||||
      startAll;
 | 
			
		||||
      $machine->waitForUnit("transmission");
 | 
			
		||||
      $machine->shutdown;
 | 
			
		||||
      start_all()
 | 
			
		||||
      machine.wait_for_unit("transmission")
 | 
			
		||||
      machine.shutdown()
 | 
			
		||||
    '';
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ let
 | 
			
		||||
  wg-snakeoil-keys = import ./snakeoil-keys.nix;
 | 
			
		||||
in
 | 
			
		||||
 | 
			
		||||
import ../make-test.nix ({ pkgs, ...} : {
 | 
			
		||||
import ../make-test-python.nix ({ pkgs, ...} : {
 | 
			
		||||
  name = "wireguard";
 | 
			
		||||
  meta = with pkgs.stdenv.lib.maintainers; {
 | 
			
		||||
    maintainers = [ ma27 ];
 | 
			
		||||
@ -86,12 +86,12 @@ import ../make-test.nix ({ pkgs, ...} : {
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  testScript = ''
 | 
			
		||||
    startAll;
 | 
			
		||||
    start_all()
 | 
			
		||||
 | 
			
		||||
    $peer0->waitForUnit("wireguard-wg0.service");
 | 
			
		||||
    $peer1->waitForUnit("wireguard-wg0.service");
 | 
			
		||||
    peer0.wait_for_unit("wireguard-wg0.service")
 | 
			
		||||
    peer1.wait_for_unit("wireguard-wg0.service")
 | 
			
		||||
 | 
			
		||||
    $peer1->succeed("ping -c5 fc00::1");
 | 
			
		||||
    $peer1->succeed("ping -c5 10.23.42.1")
 | 
			
		||||
    peer1.succeed("ping -c5 fc00::1")
 | 
			
		||||
    peer1.succeed("ping -c5 10.23.42.1")
 | 
			
		||||
  '';
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import ../make-test.nix ({ pkgs, ...} : {
 | 
			
		||||
import ../make-test-python.nix ({ pkgs, ...} : {
 | 
			
		||||
  name = "wireguard-generated";
 | 
			
		||||
  meta = with pkgs.stdenv.lib.maintainers; {
 | 
			
		||||
    maintainers = [ ma27 grahamc ];
 | 
			
		||||
@ -28,30 +28,34 @@ import ../make-test.nix ({ pkgs, ...} : {
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  testScript = ''
 | 
			
		||||
    startAll;
 | 
			
		||||
    start_all()
 | 
			
		||||
 | 
			
		||||
    $peer1->waitForUnit("wireguard-wg0.service");
 | 
			
		||||
    $peer2->waitForUnit("wireguard-wg0.service");
 | 
			
		||||
    peer1.wait_for_unit("wireguard-wg0.service")
 | 
			
		||||
    peer2.wait_for_unit("wireguard-wg0.service")
 | 
			
		||||
 | 
			
		||||
    my ($retcode, $peer1pubkey) = $peer1->execute("wg pubkey < /etc/wireguard/private");
 | 
			
		||||
    $peer1pubkey =~ s/\s+$//;
 | 
			
		||||
    if ($retcode != 0) {
 | 
			
		||||
      die "Could not read public key from peer1";
 | 
			
		||||
    }
 | 
			
		||||
    retcode, peer1pubkey = peer1.execute("wg pubkey < /etc/wireguard/private")
 | 
			
		||||
    if retcode != 0:
 | 
			
		||||
        raise Exception("Could not read public key from peer1")
 | 
			
		||||
 | 
			
		||||
    my ($retcode, $peer2pubkey) = $peer2->execute("wg pubkey < /etc/wireguard/private");
 | 
			
		||||
    $peer2pubkey =~ s/\s+$//;
 | 
			
		||||
    if ($retcode != 0) {
 | 
			
		||||
      die "Could not read public key from peer2";
 | 
			
		||||
    }
 | 
			
		||||
    retcode, peer2pubkey = peer2.execute("wg pubkey < /etc/wireguard/private")
 | 
			
		||||
    if retcode != 0:
 | 
			
		||||
        raise Exception("Could not read public key from peer2")
 | 
			
		||||
 | 
			
		||||
    $peer1->succeed("wg set wg0 peer $peer2pubkey allowed-ips 10.10.10.2/32 endpoint 192.168.1.2:12345 persistent-keepalive 1");
 | 
			
		||||
    $peer1->succeed("ip route replace 10.10.10.2/32 dev wg0 table main");
 | 
			
		||||
    peer1.succeed(
 | 
			
		||||
        "wg set wg0 peer {} allowed-ips 10.10.10.2/32 endpoint 192.168.1.2:12345 persistent-keepalive 1".format(
 | 
			
		||||
            peer2pubkey.strip()
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    peer1.succeed("ip route replace 10.10.10.2/32 dev wg0 table main")
 | 
			
		||||
 | 
			
		||||
    $peer2->succeed("wg set wg0 peer $peer1pubkey allowed-ips 10.10.10.1/32 endpoint 192.168.1.1:12345 persistent-keepalive 1");
 | 
			
		||||
    $peer2->succeed("ip route replace 10.10.10.1/32 dev wg0 table main");
 | 
			
		||||
    peer2.succeed(
 | 
			
		||||
        "wg set wg0 peer {} allowed-ips 10.10.10.1/32 endpoint 192.168.1.1:12345 persistent-keepalive 1".format(
 | 
			
		||||
            peer1pubkey.strip()
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    peer2.succeed("ip route replace 10.10.10.1/32 dev wg0 table main")
 | 
			
		||||
 | 
			
		||||
    $peer1->succeed("ping -c1 10.10.10.2");
 | 
			
		||||
    $peer2->succeed("ping -c1 10.10.10.1");
 | 
			
		||||
    peer1.succeed("ping -c1 10.10.10.2")
 | 
			
		||||
    peer2.succeed("ping -c1 10.10.10.1")
 | 
			
		||||
  '';
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@ with import ../lib/testing.nix { inherit system pkgs; };
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
 | 
			
		||||
  makeTest = import ./make-test.nix;
 | 
			
		||||
  makeTest = import ./make-test-python.nix;
 | 
			
		||||
 | 
			
		||||
  makeZfsTest = name:
 | 
			
		||||
    { kernelPackage ? pkgs.linuxPackages_latest
 | 
			
		||||
@ -34,12 +34,12 @@ let
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
      testScript = ''
 | 
			
		||||
        $machine->succeed("modprobe zfs");
 | 
			
		||||
        $machine->succeed("zpool status");
 | 
			
		||||
        machine.succeed("modprobe zfs")
 | 
			
		||||
        machine.succeed("zpool status")
 | 
			
		||||
 | 
			
		||||
        $machine->succeed("ls /dev");
 | 
			
		||||
        machine.succeed("ls /dev")
 | 
			
		||||
 | 
			
		||||
        $machine->succeed(
 | 
			
		||||
        machine.succeed(
 | 
			
		||||
          "mkdir /tmp/mnt",
 | 
			
		||||
 | 
			
		||||
          "udevadm settle",
 | 
			
		||||
@ -55,9 +55,7 @@ let
 | 
			
		||||
          "umount /tmp/mnt",
 | 
			
		||||
          "zpool destroy rpool",
 | 
			
		||||
          "udevadm settle"
 | 
			
		||||
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        )
 | 
			
		||||
      '' + extraTest;
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
@ -70,8 +68,8 @@ in {
 | 
			
		||||
  unstable = makeZfsTest "unstable" {
 | 
			
		||||
    enableUnstable = true;
 | 
			
		||||
    extraTest = ''
 | 
			
		||||
      $machine->succeed(
 | 
			
		||||
        "echo password | zpool create -o altroot='/tmp/mnt' -O encryption=aes-256-gcm -O keyformat=passphrase rpool /dev/vdb1",
 | 
			
		||||
      machine.succeed(
 | 
			
		||||
        "echo password | zpool create -o altroot=\"/tmp/mnt\" -O encryption=aes-256-gcm -O keyformat=passphrase rpool /dev/vdb1",
 | 
			
		||||
        "zfs create -o mountpoint=legacy rpool/root",
 | 
			
		||||
        "mount -t zfs rpool/root /tmp/mnt",
 | 
			
		||||
        "udevadm settle",
 | 
			
		||||
@ -79,7 +77,7 @@ in {
 | 
			
		||||
        "umount /tmp/mnt",
 | 
			
		||||
        "zpool destroy rpool",
 | 
			
		||||
        "udevadm settle"
 | 
			
		||||
      );
 | 
			
		||||
      )
 | 
			
		||||
    '';
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,15 +12,15 @@
 | 
			
		||||
with stdenv.lib;
 | 
			
		||||
 | 
			
		||||
stdenv.mkDerivation rec {
 | 
			
		||||
  version = "2.9.4";
 | 
			
		||||
  version = "2.9.5";
 | 
			
		||||
  pname = "asunder";
 | 
			
		||||
  src = fetchurl {
 | 
			
		||||
    url = "http://littlesvr.ca/asunder/releases/${pname}-${version}.tar.bz2";
 | 
			
		||||
    sha256 = "1bwc9v9l1f3kqjd7wis6g2sv6ibc618ybh0gsb8mkkfhadp68w30";
 | 
			
		||||
    sha256 = "069x6az2r3wlb2hd07iz0hxpxwknw7s9h7pyhnkmzv1pw9ci3kk4";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  nativeBuildInputs = [ pkgconfig ];
 | 
			
		||||
  buildInputs = [ gtk2 libcddb intltool makeWrapper ];
 | 
			
		||||
  nativeBuildInputs = [ intltool makeWrapper pkgconfig ];
 | 
			
		||||
  buildInputs = [ gtk2 libcddb ];
 | 
			
		||||
 | 
			
		||||
  runtimeDeps =
 | 
			
		||||
    optional mp3Support lame ++
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@
 | 
			
		||||
 | 
			
		||||
python3.pkgs.buildPythonApplication rec  {
 | 
			
		||||
  pname = "lollypop";
 | 
			
		||||
  version = "1.2.2";
 | 
			
		||||
  version = "1.2.5";
 | 
			
		||||
 | 
			
		||||
  format = "other";
 | 
			
		||||
  doCheck = false;
 | 
			
		||||
@ -28,7 +28,7 @@ python3.pkgs.buildPythonApplication rec  {
 | 
			
		||||
    url = "https://gitlab.gnome.org/World/lollypop";
 | 
			
		||||
    rev = "refs/tags/${version}";
 | 
			
		||||
    fetchSubmodules = true;
 | 
			
		||||
    sha256 = "02dgp3b10yaw0yqzdzd15msjgxayvjkg9m652is0d7rwgjq1pk6v";
 | 
			
		||||
    sha256 = "148p3ab7nnfz13hgjkx1cf2ahq9mgl72csrl35xy6d0nkfqbfr8r";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  nativeBuildInputs = [
 | 
			
		||||
 | 
			
		||||
@ -6,11 +6,11 @@ assert stdenv ? glibc;
 | 
			
		||||
 | 
			
		||||
stdenv.mkDerivation  rec {
 | 
			
		||||
  pname = "yoshimi";
 | 
			
		||||
  version = "1.6.0.1";
 | 
			
		||||
  version = "1.6.0.2";
 | 
			
		||||
 | 
			
		||||
  src = fetchurl {
 | 
			
		||||
    url = "mirror://sourceforge/yoshimi/${pname}-${version}.tar.bz2";
 | 
			
		||||
    sha256 = "140f2k4akj39pny8c7i794q125415gyvmy4rday0il5ncp3glik4";
 | 
			
		||||
    sha256 = "0q2cw168r53r50zghkdqcxba2cybn44axbdkwacvkm7ag2z0j2l8";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  buildInputs = [
 | 
			
		||||
 | 
			
		||||
@ -8,14 +8,14 @@ let
 | 
			
		||||
    inherit (gnome2) GConf gnome_vfs;
 | 
			
		||||
  };
 | 
			
		||||
  stableVersion = {
 | 
			
		||||
    version = "3.5.1.0"; # "Android Studio 3.5.1"
 | 
			
		||||
    build = "191.5900203";
 | 
			
		||||
    sha256Hash = "0afxlif8pkrl6m1lhiqri1qv4vf5mfm1yg6qk5rad0442hm3kz4l";
 | 
			
		||||
    version = "3.5.2.0"; # "Android Studio 3.5.2"
 | 
			
		||||
    build = "191.5977832";
 | 
			
		||||
    sha256Hash = "0kcd6kd5rn4b76damkfddin18d1r0dck05piv8mq1ns7x1n4hf7q";
 | 
			
		||||
  };
 | 
			
		||||
  betaVersion = {
 | 
			
		||||
    version = "3.6.0.13"; # "Android Studio 3.6 Beta 1"
 | 
			
		||||
    build = "192.5916306";
 | 
			
		||||
    sha256Hash = "0kvz3mgpfb3wqr1pw9847d5syswlzls3b4nilzgk6w127k2zmkfy";
 | 
			
		||||
    version = "3.6.0.14"; # "Android Studio 3.6 Beta 2"
 | 
			
		||||
    build = "192.5947919";
 | 
			
		||||
    sha256Hash = "09l7mdjkzwnkkcgxp0x66bzm125ignrfssy7n141wvs2rd66i2fs";
 | 
			
		||||
  };
 | 
			
		||||
  latestVersion = { # canary & dev
 | 
			
		||||
    version = "4.0.0.1"; # "Android Studio 4.0 Canary 1"
 | 
			
		||||
 | 
			
		||||
@ -4,13 +4,13 @@
 | 
			
		||||
 | 
			
		||||
stdenv.mkDerivation rec {
 | 
			
		||||
  pname = "quilter";
 | 
			
		||||
  version = "2.0.2";
 | 
			
		||||
  version = "2.0.3";
 | 
			
		||||
 | 
			
		||||
  src = fetchFromGitHub {
 | 
			
		||||
    owner = "lainsce";
 | 
			
		||||
    repo = pname;
 | 
			
		||||
    rev = version;
 | 
			
		||||
    sha256 = "0qd8qssqzds06l08f4yf39i3bjl1ljyr85wgc3yn6mn698ynx30g";
 | 
			
		||||
    sha256 = "13l8z3bchha4ax14s48pcqdxh8gnj4mlvv06lk9dwk9fplc93821";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  nativeBuildInputs = [
 | 
			
		||||
 | 
			
		||||
@ -5,11 +5,11 @@
 | 
			
		||||
 | 
			
		||||
stdenv.mkDerivation rec {
 | 
			
		||||
  pname = "avocode";
 | 
			
		||||
  version = "3.9.3";
 | 
			
		||||
  version = "3.9.6";
 | 
			
		||||
 | 
			
		||||
  src = fetchurl {
 | 
			
		||||
    url = "https://media.avocode.com/download/avocode-app/${version}/avocode-${version}-linux.zip";
 | 
			
		||||
    sha256 = "1ki2fpn70p1rzf52q8511a90n7y7dqi86fs2a48qhass1abxlpqx";
 | 
			
		||||
    sha256 = "0jnl461dg2s5panrw12707bv34g6wxc1pxc90awnja13yq0z6bfc";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  libPath = stdenv.lib.makeLibraryPath (with xorg; [
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
{ stdenv, mkDerivation, fetchurl, fetchpatch, cmake, ninja, coin3d, xercesc, ode
 | 
			
		||||
{ stdenv, mkDerivation, fetchFromGitHub, fetchpatch, cmake, ninja, coin3d, xercesc, ode
 | 
			
		||||
, eigen, qtbase, qttools, qtwebkit, opencascade-occt, gts, hdf5, vtk, medfile
 | 
			
		||||
, zlib, python3Packages, swig, gfortran, libXmu, soqt, libf2c, libGLU
 | 
			
		||||
, makeWrapper, pkgconfig, mpi ? null }:
 | 
			
		||||
@ -9,11 +9,13 @@ let
 | 
			
		||||
  pythonPackages = python3Packages;
 | 
			
		||||
in mkDerivation rec {
 | 
			
		||||
  pname = "freecad";
 | 
			
		||||
  version = "0.18.3";
 | 
			
		||||
  version = "0.18.4";
 | 
			
		||||
 | 
			
		||||
  src = fetchurl {
 | 
			
		||||
    url = "https://github.com/FreeCAD/FreeCAD/archive/${version}.tar.gz";
 | 
			
		||||
    sha256 = "07j7azgnicmd8cqnyskp15y44ykgj5qqz5y3w1jdynrv3yrvk1kz";
 | 
			
		||||
  src = fetchFromGitHub {
 | 
			
		||||
    owner = "FreeCAD";
 | 
			
		||||
    repo = "FreeCAD";
 | 
			
		||||
    rev = version;
 | 
			
		||||
    sha256 = "1phs9a0px5fnzpyx930cz39p5dis0f0yajxzii3c3sazgkzrd55s";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  nativeBuildInputs = [ cmake ninja pkgconfig pythonPackages.pyside2-tools ];
 | 
			
		||||
 | 
			
		||||
@ -1,31 +1,118 @@
 | 
			
		||||
{ stdenv, fetchurl, substituteAll, pkgconfig, intltool, babl, gegl, gtk2, glib, gdk-pixbuf, isocodes
 | 
			
		||||
, pango, cairo, freetype, fontconfig, lcms, libpng, libjpeg, poppler, poppler_data, libtiff
 | 
			
		||||
, libmng, librsvg, libwmf, zlib, libzip, ghostscript, aalib, shared-mime-info
 | 
			
		||||
, python2Packages, libexif, gettext, xorg, glib-networking, libmypaint, gexiv2
 | 
			
		||||
, harfbuzz, mypaint-brushes, libwebp, libheif, libgudev, openexr
 | 
			
		||||
, AppKit, Cocoa, gtk-mac-integration-gtk2 }:
 | 
			
		||||
{ stdenv
 | 
			
		||||
, lib
 | 
			
		||||
, fetchurl
 | 
			
		||||
, substituteAll
 | 
			
		||||
, pkgconfig
 | 
			
		||||
, intltool
 | 
			
		||||
, babl
 | 
			
		||||
, gegl
 | 
			
		||||
, gtk2
 | 
			
		||||
, glib
 | 
			
		||||
, gdk-pixbuf
 | 
			
		||||
, isocodes
 | 
			
		||||
, pango
 | 
			
		||||
, cairo
 | 
			
		||||
, freetype
 | 
			
		||||
, fontconfig
 | 
			
		||||
, lcms
 | 
			
		||||
, libpng
 | 
			
		||||
, libjpeg
 | 
			
		||||
, poppler
 | 
			
		||||
, poppler_data
 | 
			
		||||
, libtiff
 | 
			
		||||
, libmng
 | 
			
		||||
, librsvg
 | 
			
		||||
, libwmf
 | 
			
		||||
, zlib
 | 
			
		||||
, libzip
 | 
			
		||||
, ghostscript
 | 
			
		||||
, aalib
 | 
			
		||||
, shared-mime-info
 | 
			
		||||
, python2Packages
 | 
			
		||||
, libexif
 | 
			
		||||
, gettext
 | 
			
		||||
, xorg
 | 
			
		||||
, glib-networking
 | 
			
		||||
, libmypaint
 | 
			
		||||
, gexiv2
 | 
			
		||||
, harfbuzz
 | 
			
		||||
, mypaint-brushes
 | 
			
		||||
, libwebp
 | 
			
		||||
, libheif
 | 
			
		||||
, libgudev
 | 
			
		||||
, openexr
 | 
			
		||||
, AppKit
 | 
			
		||||
, Cocoa
 | 
			
		||||
, gtk-mac-integration-gtk2
 | 
			
		||||
}:
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
  inherit (python2Packages) pygtk wrapPython python;
 | 
			
		||||
in stdenv.mkDerivation rec {
 | 
			
		||||
  pname = "gimp";
 | 
			
		||||
  version = "2.10.12";
 | 
			
		||||
  version = "2.10.14";
 | 
			
		||||
 | 
			
		||||
  src = fetchurl {
 | 
			
		||||
    url = "http://download.gimp.org/pub/gimp/v${stdenv.lib.versions.majorMinor version}/${pname}-${version}.tar.bz2";
 | 
			
		||||
    sha256 = "0wdcr8d2ink4swn5r4v13bsiya6s3xm4ya97sdbhs4l40y7bb03x";
 | 
			
		||||
    url = "http://download.gimp.org/pub/gimp/v${lib.versions.majorMinor version}/${pname}-${version}.tar.bz2";
 | 
			
		||||
    sha256 = "0m6wdnfvsxyhimdd4v3351g4r1fklllnbipbwcfym3h7q88hz6yz";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  nativeBuildInputs = [ pkgconfig intltool gettext wrapPython ];
 | 
			
		||||
  propagatedBuildInputs = [ gegl ]; # needed by gimp-2.0.pc
 | 
			
		||||
  nativeBuildInputs = [
 | 
			
		||||
    pkgconfig
 | 
			
		||||
    intltool
 | 
			
		||||
    gettext
 | 
			
		||||
    wrapPython
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  buildInputs = [
 | 
			
		||||
    babl gegl gtk2 glib gdk-pixbuf pango cairo gexiv2 harfbuzz isocodes
 | 
			
		||||
    freetype fontconfig lcms libpng libjpeg poppler poppler_data libtiff openexr
 | 
			
		||||
    libmng librsvg libwmf zlib libzip ghostscript aalib shared-mime-info libwebp libheif
 | 
			
		||||
    python pygtk libexif xorg.libXpm glib-networking libmypaint mypaint-brushes
 | 
			
		||||
  ] ++ stdenv.lib.optionals stdenv.isDarwin [
 | 
			
		||||
    AppKit Cocoa gtk-mac-integration-gtk2
 | 
			
		||||
  ] ++ stdenv.lib.optionals stdenv.isLinux [ libgudev ];
 | 
			
		||||
    babl
 | 
			
		||||
    gegl
 | 
			
		||||
    gtk2
 | 
			
		||||
    glib
 | 
			
		||||
    gdk-pixbuf
 | 
			
		||||
    pango
 | 
			
		||||
    cairo
 | 
			
		||||
    gexiv2
 | 
			
		||||
    harfbuzz
 | 
			
		||||
    isocodes
 | 
			
		||||
    freetype
 | 
			
		||||
    fontconfig
 | 
			
		||||
    lcms
 | 
			
		||||
    libpng
 | 
			
		||||
    libjpeg
 | 
			
		||||
    poppler
 | 
			
		||||
    poppler_data
 | 
			
		||||
    libtiff
 | 
			
		||||
    openexr
 | 
			
		||||
    libmng
 | 
			
		||||
    librsvg
 | 
			
		||||
    libwmf
 | 
			
		||||
    zlib
 | 
			
		||||
    libzip
 | 
			
		||||
    ghostscript
 | 
			
		||||
    aalib
 | 
			
		||||
    shared-mime-info
 | 
			
		||||
    libwebp
 | 
			
		||||
    libheif
 | 
			
		||||
    python
 | 
			
		||||
    pygtk
 | 
			
		||||
    libexif
 | 
			
		||||
    xorg.libXpm
 | 
			
		||||
    glib-networking
 | 
			
		||||
    libmypaint
 | 
			
		||||
    mypaint-brushes
 | 
			
		||||
  ] ++ lib.optionals stdenv.isDarwin [
 | 
			
		||||
    AppKit
 | 
			
		||||
    Cocoa
 | 
			
		||||
    gtk-mac-integration-gtk2
 | 
			
		||||
  ] ++ lib.optionals stdenv.isLinux [
 | 
			
		||||
    libgudev
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  # needed by gimp-2.0.pc
 | 
			
		||||
  propagatedBuildInputs = [
 | 
			
		||||
    gegl
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  pythonPath = [ pygtk ];
 | 
			
		||||
 | 
			
		||||
@ -48,7 +135,7 @@ in stdenv.mkDerivation rec {
 | 
			
		||||
 | 
			
		||||
  postFixup = ''
 | 
			
		||||
    wrapPythonProgramsIn $out/lib/gimp/${passthru.majorVersion}/plug-ins/
 | 
			
		||||
    wrapProgram $out/bin/gimp-${stdenv.lib.versions.majorMinor version} \
 | 
			
		||||
    wrapProgram $out/bin/gimp-${lib.versions.majorMinor version} \
 | 
			
		||||
      --prefix PYTHONPATH : "$PYTHONPATH" \
 | 
			
		||||
      --set GDK_PIXBUF_MODULE_FILE "$GDK_PIXBUF_MODULE_FILE"
 | 
			
		||||
  '';
 | 
			
		||||
@ -56,9 +143,9 @@ in stdenv.mkDerivation rec {
 | 
			
		||||
  passthru = rec {
 | 
			
		||||
    # The declarations for `gimp-with-plugins` wrapper,
 | 
			
		||||
    # used for determining plug-in installation paths
 | 
			
		||||
    majorVersion = "${stdenv.lib.versions.major version}.0";
 | 
			
		||||
    majorVersion = "${lib.versions.major version}.0";
 | 
			
		||||
    targetPluginDir = "lib/gimp/${majorVersion}/plug-ins";
 | 
			
		||||
    targetScriptDir = "lib/gimp/${majorVersion}/scripts";
 | 
			
		||||
    targetScriptDir = "share/gimp/${majorVersion}/scripts";
 | 
			
		||||
 | 
			
		||||
    # probably its a good idea to use the same gtk in plugins ?
 | 
			
		||||
    gtk = gtk2;
 | 
			
		||||
@ -76,9 +163,9 @@ in stdenv.mkDerivation rec {
 | 
			
		||||
 | 
			
		||||
  enableParallelBuilding = true;
 | 
			
		||||
 | 
			
		||||
  meta = with stdenv.lib; {
 | 
			
		||||
  meta = with lib; {
 | 
			
		||||
    description = "The GNU Image Manipulation Program";
 | 
			
		||||
    homepage = https://www.gimp.org/;
 | 
			
		||||
    homepage = "https://www.gimp.org/";
 | 
			
		||||
    maintainers = with maintainers; [ jtojnar ];
 | 
			
		||||
    license = licenses.gpl3Plus;
 | 
			
		||||
    platforms = platforms.unix;
 | 
			
		||||
 | 
			
		||||
@ -8,31 +8,33 @@ let
 | 
			
		||||
  inherit (pkgs) stdenv fetchurl pkgconfig intltool glib fetchFromGitHub;
 | 
			
		||||
  inherit (gimp) targetPluginDir targetScriptDir;
 | 
			
		||||
 | 
			
		||||
  pluginDerivation = a: stdenv.mkDerivation ({
 | 
			
		||||
  pluginDerivation = a: let
 | 
			
		||||
    name = a.name or "${a.pname}-${a.version}";
 | 
			
		||||
  in stdenv.mkDerivation ({
 | 
			
		||||
    prePhases = "extraLib";
 | 
			
		||||
    extraLib = ''
 | 
			
		||||
      installScripts(){
 | 
			
		||||
        mkdir -p $out/${targetScriptDir};
 | 
			
		||||
        for p in "$@"; do cp "$p" $out/${targetScriptDir}; done
 | 
			
		||||
        mkdir -p $out/${targetScriptDir}/${name};
 | 
			
		||||
        for p in "$@"; do cp "$p" -r $out/${targetScriptDir}/${name}; done
 | 
			
		||||
      }
 | 
			
		||||
      installPlugins(){
 | 
			
		||||
        mkdir -p $out/${targetPluginDir};
 | 
			
		||||
        for p in "$@"; do cp "$p" $out/${targetPluginDir}; done
 | 
			
		||||
        mkdir -p $out/${targetPluginDir}/${name};
 | 
			
		||||
        for p in "$@"; do cp "$p" -r $out/${targetPluginDir}/${name}; done
 | 
			
		||||
      }
 | 
			
		||||
    '';
 | 
			
		||||
  }
 | 
			
		||||
  // a
 | 
			
		||||
  // {
 | 
			
		||||
      name = "gimp-plugin-${a.name or "${a.pname}-${a.version}"}";
 | 
			
		||||
      name = "gimp-plugin-${name}";
 | 
			
		||||
      buildInputs = [ gimp gimp.gtk glib ] ++ (a.buildInputs or []);
 | 
			
		||||
      nativeBuildInputs = [ pkgconfig intltool ] ++ (a.nativeBuildInputs or []);
 | 
			
		||||
    }
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  scriptDerivation = {name, src} : pluginDerivation {
 | 
			
		||||
    inherit name; phases = "extraLib installPhase";
 | 
			
		||||
  scriptDerivation = {src, ...}@attrs : pluginDerivation ({
 | 
			
		||||
    phases = [ "extraLib" "installPhase" ];
 | 
			
		||||
    installPhase = "installScripts ${src}";
 | 
			
		||||
  };
 | 
			
		||||
  } // attrs);
 | 
			
		||||
 | 
			
		||||
in
 | 
			
		||||
 | 
			
		||||
@ -46,6 +48,7 @@ stdenv.lib.makeScope pkgs.newScope (self: with self; {
 | 
			
		||||
      url = https://ftp.gimp.org/pub/gimp/plug-ins/v2.6/gap/gimp-gap-2.6.0.tar.bz2;
 | 
			
		||||
      sha256 = "1jic7ixcmsn4kx2cn32nc5087rk6g8xsrz022xy11yfmgvhzb0ql";
 | 
			
		||||
    };
 | 
			
		||||
    NIX_LDFLAGS = [ "-lm" ];
 | 
			
		||||
    patchPhase = ''
 | 
			
		||||
      sed -e 's,^\(GIMP_PLUGIN_DIR=\).*,\1'"$out/${gimp.name}-plugins", \
 | 
			
		||||
       -e 's,^\(GIMP_DATA_DIR=\).*,\1'"$out/share/${gimp.name}", -i configure
 | 
			
		||||
@ -131,6 +134,7 @@ stdenv.lib.makeScope pkgs.newScope (self: with self; {
 | 
			
		||||
      Filters/Enhance/Wavelet sharpen
 | 
			
		||||
    */
 | 
			
		||||
    name = "wavelet-sharpen-0.1.2";
 | 
			
		||||
    NIX_LDFLAGS = [ "-lm" ];
 | 
			
		||||
    src = fetchurl {
 | 
			
		||||
      url = http://registry.gimp.org/files/wavelet-sharpen-0.1.2.tar.gz;
 | 
			
		||||
      sha256 = "0vql1k67i21g5ivaa1jh56rg427m0icrkpryrhg75nscpirfxxqw";
 | 
			
		||||
@ -195,6 +199,7 @@ stdenv.lib.makeScope pkgs.newScope (self: with self; {
 | 
			
		||||
      url = http://tir.astro.utoledo.edu/jdsmith/code/eb/exposure-blend.scm;
 | 
			
		||||
      sha256 = "1b6c9wzpklqras4wwsyw3y3jp6fjmhnnskqiwm5sabs8djknfxla";
 | 
			
		||||
    };
 | 
			
		||||
    meta.broken = true;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  lightning = scriptDerivation {
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ in symlinkJoin {
 | 
			
		||||
    for each in gimp-${versionBranch} gimp-console-${versionBranch}; do
 | 
			
		||||
      wrapProgram $out/bin/$each \
 | 
			
		||||
        --set GIMP2_PLUGINDIR "$out/lib/gimp/2.0" \
 | 
			
		||||
        --set GIMP2_DATADIR "$out/share/gimp/2.0" \
 | 
			
		||||
        --prefix GTK_PATH : "${gnome3.gnome-themes-extra}/lib/gtk-2.0" \
 | 
			
		||||
        ${toString extraArgs}
 | 
			
		||||
    done
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
{ stdenv, fetchurl, pkgconfig, perlPackages, libXft
 | 
			
		||||
, libpng, zlib, popt, boehmgc, libxml2, libxslt, glib, gtkmm2
 | 
			
		||||
, glibmm, libsigcxx, lcms, boost, gettext, makeWrapper
 | 
			
		||||
, gsl, gtkspell2, python2, poppler, imagemagick, libwpg, librevenge
 | 
			
		||||
, gsl, gtkspell2, cairo, python2, poppler, imagemagick, libwpg, librevenge
 | 
			
		||||
, libvisio, libcdr, libexif, potrace, cmake
 | 
			
		||||
, librsvg, wrapGAppsHook
 | 
			
		||||
}:
 | 
			
		||||
@ -52,7 +52,8 @@ stdenv.mkDerivation rec {
 | 
			
		||||
    librsvg # for loading icons
 | 
			
		||||
 | 
			
		||||
    python2Env perlPackages.perl
 | 
			
		||||
  ] ++ stdenv.lib.optional (!stdenv.isDarwin) gtkspell2;
 | 
			
		||||
  ] ++ stdenv.lib.optional (!stdenv.isDarwin) gtkspell2
 | 
			
		||||
    ++ stdenv.lib.optional stdenv.isDarwin cairo;
 | 
			
		||||
 | 
			
		||||
  enableParallelBuilding = true;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -47,7 +47,7 @@ mkDerivation rec {
 | 
			
		||||
    poppler_utils libpng imagemagick libjpeg
 | 
			
		||||
    fontconfig podofo qtbase chmlib icu sqlite libusb1 libmtp xdg_utils
 | 
			
		||||
  ] ++ (with pypkgs; [
 | 
			
		||||
    apsw cssselect css-parser dateutil dnspython html5-parser lxml netifaces pillow
 | 
			
		||||
    apsw cssselect css-parser dateutil dnspython html5-parser lxml markdown netifaces pillow
 | 
			
		||||
    python pyqt5_with_qtwebkit sip
 | 
			
		||||
    regex msgpack beautifulsoup4 html2text
 | 
			
		||||
    # the following are distributed with calibre, but we use upstream instead
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@
 | 
			
		||||
 | 
			
		||||
stdenv.mkDerivation rec {
 | 
			
		||||
  pname = "dbeaver-ce";
 | 
			
		||||
  version = "6.2.3";
 | 
			
		||||
  version = "6.2.4";
 | 
			
		||||
 | 
			
		||||
  desktopItem = makeDesktopItem {
 | 
			
		||||
    name = "dbeaver";
 | 
			
		||||
@ -30,7 +30,7 @@ stdenv.mkDerivation rec {
 | 
			
		||||
 | 
			
		||||
  src = fetchurl {
 | 
			
		||||
    url = "https://dbeaver.io/files/${version}/dbeaver-ce-${version}-linux.gtk.x86_64.tar.gz";
 | 
			
		||||
    sha256 = "1v4sllzvaz4fj8s14ddzw11wczlghbdppv8fl5jg6xglg687sgaj";
 | 
			
		||||
    sha256 = "1k3aan290kfy2b53gl8r4yxvb8jas6sms1r052m3jld3i8frqgva";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  installPhase = ''
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,32 @@
 | 
			
		||||
{ stdenv, buildEnv, fetchzip, mono }:
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
  version = "2.6";
 | 
			
		||||
  drv = stdenv.mkDerivation {
 | 
			
		||||
    pname = "otpkeyprov";
 | 
			
		||||
    inherit version;
 | 
			
		||||
 | 
			
		||||
    src = fetchzip {
 | 
			
		||||
      url = "https://keepass.info/extensions/v2/otpkeyprov/OtpKeyProv-${version}.zip";
 | 
			
		||||
      sha256 = "1p60k55v2sxnv1varmp0dgbsi2rhjg9kj19cf54mkc87nss5h1ki";
 | 
			
		||||
      stripRoot = false;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    meta = {
 | 
			
		||||
      description = "OtpKeyProv is a key provider based on one-time passwords";
 | 
			
		||||
      homepage    = "https://keepass.info/plugins.html#otpkeyprov";
 | 
			
		||||
      platforms   = with stdenv.lib.platforms; linux;
 | 
			
		||||
      license     = stdenv.lib.licenses.gpl2;
 | 
			
		||||
      maintainers = [ stdenv.lib.maintainers.ente ];
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    pluginFilename = "OtpKeyProv.plgx";
 | 
			
		||||
 | 
			
		||||
    installPhase = ''
 | 
			
		||||
      mkdir -p $out/lib/dotnet/keepass/
 | 
			
		||||
      cp $pluginFilename $out/lib/dotnet/keepass/$pluginFilename
 | 
			
		||||
    '';
 | 
			
		||||
  };
 | 
			
		||||
in
 | 
			
		||||
  # Mono is required to compile plugin at runtime, after loading.
 | 
			
		||||
  buildEnv { name = drv.name; paths = [ mono drv ]; }
 | 
			
		||||
@ -19,6 +19,6 @@ stdenv.mkDerivation rec {
 | 
			
		||||
    description = "Build system for netsurf browser";
 | 
			
		||||
    license = licenses.gpl2;
 | 
			
		||||
    maintainers = [ maintainers.vrthra ];
 | 
			
		||||
    platforms = platforms.linux;
 | 
			
		||||
    platforms = platforms.unix;
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,6 @@ stdenv.mkDerivation rec {
 | 
			
		||||
    description = "GIF Decoder for netsurf browser";
 | 
			
		||||
    license = licenses.gpl2;
 | 
			
		||||
    maintainers = [ maintainers.vrthra ];
 | 
			
		||||
    platforms = platforms.linux;
 | 
			
		||||
    platforms = platforms.unix;
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,10 +6,10 @@ let
 | 
			
		||||
 | 
			
		||||
  pname = "simplenote";
 | 
			
		||||
 | 
			
		||||
  version = "1.8.0";
 | 
			
		||||
  version = "1.9.1";
 | 
			
		||||
 | 
			
		||||
  sha256 = {
 | 
			
		||||
    x86_64-linux = "066gr1awdj5nwdr1z57mmvx7dd1z19g0wzsgbnrrb89bqfj67ykl";
 | 
			
		||||
    x86_64-linux = "1zqrjh1xfdpkpj1fsri9r4qkazh9j89pbj8vjr474b39v56v693j";
 | 
			
		||||
  }.${system};
 | 
			
		||||
 | 
			
		||||
  meta = with stdenv.lib; {
 | 
			
		||||
 | 
			
		||||
@ -17,11 +17,11 @@ let
 | 
			
		||||
  vivaldiName = if isSnapshot then "vivaldi-snapshot" else "vivaldi";
 | 
			
		||||
in stdenv.mkDerivation rec {
 | 
			
		||||
  pname = "vivaldi";
 | 
			
		||||
  version = "2.9.1705.31-1";
 | 
			
		||||
  version = "2.9.1705.38-1";
 | 
			
		||||
 | 
			
		||||
  src = fetchurl {
 | 
			
		||||
    url = "https://downloads.vivaldi.com/${branch}/vivaldi-${branch}_${version}_amd64.deb";
 | 
			
		||||
    sha256 = "113bycfygyx09bc5bgsmdniffp3282004yrl7gr16dssxrw52al2";
 | 
			
		||||
    sha256 = "0jj2kfdl4788l132ncz3jf1pnjig7dc9gaxjmgv51n1ahmmx8shi";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  unpackPhase = ''
 | 
			
		||||
 | 
			
		||||
@ -1,16 +1,16 @@
 | 
			
		||||
{ callPackage }:
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
  stableVersion = "2.2.0";
 | 
			
		||||
  previewVersion = "2.2.0";
 | 
			
		||||
  stableVersion = "2.2.2";
 | 
			
		||||
  previewVersion = "2.2.2";
 | 
			
		||||
  addVersion = args:
 | 
			
		||||
    let version = if args.stable then stableVersion else previewVersion;
 | 
			
		||||
        branch = if args.stable then "stable" else "preview";
 | 
			
		||||
    in args // { inherit version branch; };
 | 
			
		||||
  mkGui = args: callPackage (import ./gui.nix (addVersion args)) { };
 | 
			
		||||
  mkServer = args: callPackage (import ./server.nix (addVersion args)) { };
 | 
			
		||||
  guiSrcHash = "0xghldzk126ly49y7drp241w7c0h9fb0ags9blk0rlq99i72as78";
 | 
			
		||||
  serverSrcHash = "0iphs0w6r9s85cgd95bh6jd0224ywilrzb7a4jjwi38z7a7id4gk";
 | 
			
		||||
  guiSrcHash = "0i335fjbadixp39l75w0fl5iwz2cb8rcdj2xvx1my3vzhg8lijfl";
 | 
			
		||||
  serverSrcHash = "1g6km8jc53y8ph14ifjxscbimdxma6bw5ir9gqzvkjn39k9fy1w6";
 | 
			
		||||
in {
 | 
			
		||||
  guiStable = mkGui {
 | 
			
		||||
    stable = true;
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,20 @@
 | 
			
		||||
{ stable, branch, version, sha256Hash }:
 | 
			
		||||
 | 
			
		||||
{ stdenv, python3Packages, fetchFromGitHub }:
 | 
			
		||||
{ stdenv, python3, fetchFromGitHub }:
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
  pythonPackages = python3Packages;
 | 
			
		||||
 | 
			
		||||
in pythonPackages.buildPythonPackage rec {
 | 
			
		||||
  python = python3.override {
 | 
			
		||||
    packageOverrides = self: super: {
 | 
			
		||||
      jsonschema = super.jsonschema.overridePythonAttrs (oldAttrs: rec {
 | 
			
		||||
        version = "2.6.0";
 | 
			
		||||
        src = oldAttrs.src.override {
 | 
			
		||||
          inherit version;
 | 
			
		||||
          sha256 = "00kf3zmpp9ya4sydffpifn0j0mzm342a2vzh82p6r0vh10cg7xbg";
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
in python.pkgs.buildPythonPackage rec {
 | 
			
		||||
  name = "${pname}-${version}";
 | 
			
		||||
  pname = "gns3-gui";
 | 
			
		||||
 | 
			
		||||
@ -16,7 +25,7 @@ in pythonPackages.buildPythonPackage rec {
 | 
			
		||||
    sha256 = sha256Hash;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  propagatedBuildInputs = with pythonPackages; [
 | 
			
		||||
  propagatedBuildInputs = with python.pkgs; [
 | 
			
		||||
    raven psutil jsonschema # tox for check
 | 
			
		||||
    # Runtime dependencies
 | 
			
		||||
    sip (pyqt5.override { withWebSockets = true; }) distro setuptools
 | 
			
		||||
 | 
			
		||||
@ -3,8 +3,17 @@
 | 
			
		||||
{ stdenv, python3, fetchFromGitHub }:
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
  python = python3;
 | 
			
		||||
 | 
			
		||||
  python = python3.override {
 | 
			
		||||
    packageOverrides = self: super: {
 | 
			
		||||
      jsonschema = super.jsonschema.overridePythonAttrs (oldAttrs: rec {
 | 
			
		||||
        version = "2.6.0";
 | 
			
		||||
        src = oldAttrs.src.override {
 | 
			
		||||
          inherit version;
 | 
			
		||||
          sha256 = "00kf3zmpp9ya4sydffpifn0j0mzm342a2vzh82p6r0vh10cg7xbg";
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
in python.pkgs.buildPythonPackage {
 | 
			
		||||
  pname = "gns3-server";
 | 
			
		||||
  inherit version;
 | 
			
		||||
 | 
			
		||||
@ -1,19 +1,19 @@
 | 
			
		||||
{ stdenv, fetchurl, rpmextract, autoPatchelfHook
 | 
			
		||||
, xorg, gtk2, gnome2, nss, alsaLib, udev, libnotify }:
 | 
			
		||||
, xorg, gtk3, gnome2, nss, alsaLib, udev, libnotify }:
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
  version = "4.0.1";
 | 
			
		||||
  version = "4.5.2";
 | 
			
		||||
in stdenv.mkDerivation {
 | 
			
		||||
  pname = "vk-messenger";
 | 
			
		||||
  inherit version;
 | 
			
		||||
  src = {
 | 
			
		||||
    i686-linux = fetchurl {
 | 
			
		||||
      url = "https://desktop.userapi.com/rpm/master/vk-${version}.i686.rpm";
 | 
			
		||||
      sha256 = "0mgppa9qnhix64zp40dc05yc9klsc7qiwcgw7pwq2wm7m3fz3nm8";
 | 
			
		||||
      sha256 = "11xsdmvd2diq3m61si87x2c08nap0vakcypm90wjmdjwayg3fdlw";
 | 
			
		||||
    };
 | 
			
		||||
    x86_64-linux = fetchurl {
 | 
			
		||||
      url = "https://desktop.userapi.com/rpm/master/vk-${version}.x86_64.rpm";
 | 
			
		||||
      sha256 = "0ra0y4dfx4gfa1r3lm6v42j7c9pf7a8vh12kxv3wkg3pvijwgdsm";
 | 
			
		||||
      sha256 = "0j65d6mwj6rxczi0p9fsr6jh37jxw3a3h6w67xwgdvibb7lf3gbb";
 | 
			
		||||
    };
 | 
			
		||||
  }.${stdenv.system} or (throw "Unsupported system: ${stdenv.system}");
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ in stdenv.mkDerivation {
 | 
			
		||||
  buildInputs = (with xorg; [
 | 
			
		||||
    libXdamage libXtst libXScrnSaver libxkbfile
 | 
			
		||||
  ]) ++ [
 | 
			
		||||
    gtk2 gnome2.GConf nss alsaLib
 | 
			
		||||
    gtk3 nss alsaLib
 | 
			
		||||
  ];
 | 
			
		||||
  runtimeDependencies = [ udev.lib libnotify ];
 | 
			
		||||
 | 
			
		||||
@ -49,6 +49,5 @@ in stdenv.mkDerivation {
 | 
			
		||||
    license = licenses.unfree;
 | 
			
		||||
    maintainers = [ maintainers.gnidorah ];
 | 
			
		||||
    platforms = ["i686-linux" "x86_64-linux"];
 | 
			
		||||
    hydraPlatforms = [];
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -18,12 +18,12 @@ let
 | 
			
		||||
  pname = "wire-desktop";
 | 
			
		||||
 | 
			
		||||
  version = {
 | 
			
		||||
    x86_64-linux = "3.10.2904";
 | 
			
		||||
    x86_64-linux = "3.11.2912";
 | 
			
		||||
    x86_64-darwin = "3.10.3215";
 | 
			
		||||
  }.${system} or throwSystem;
 | 
			
		||||
 | 
			
		||||
  sha256 = {
 | 
			
		||||
    x86_64-linux = "1vrz4568mlhylx17jw4z452f0vrd8yd8qkbpkcvnsbhs6k066xcn";
 | 
			
		||||
    x86_64-linux = "1d2wa13d750dd2vslnvzf0ibwjmf5s299pxq0rs2x98y2sabw3sl";
 | 
			
		||||
    x86_64-darwin = "0ygm3fgy9k1dp2kjfwsrrwq1i88wgxc6k8y80yz61ivdawgph9wa";
 | 
			
		||||
  }.${system} or throwSystem;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,10 @@ buildGoModule rec {
 | 
			
		||||
    python3.pkgs.wrapPython
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  patches = [
 | 
			
		||||
    ./runtime-sharedir.patch
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  pythonPath = [
 | 
			
		||||
    python3.pkgs.colorama
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,43 @@
 | 
			
		||||
From 7ea68a2eef026723903d72f54ca54b629881ec06 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Tadeo Kondrak <me@tadeo.ca>
 | 
			
		||||
Date: Mon, 28 Oct 2019 08:36:36 -0600
 | 
			
		||||
Subject: [PATCH] Fix aerc breaking every time the package is rebuilt.
 | 
			
		||||
 | 
			
		||||
On NixOS, the SHAREDIR changes on every rebuild to the package, but aerc
 | 
			
		||||
fills it in as part of the default config. Fix this by not substituting
 | 
			
		||||
@SHAREDIR@ in the default config until runtime.
 | 
			
		||||
---
 | 
			
		||||
 Makefile         | 2 +-
 | 
			
		||||
 config/config.go | 3 +++
 | 
			
		||||
 2 files changed, 4 insertions(+), 1 deletion(-)
 | 
			
		||||
 | 
			
		||||
diff --git a/Makefile b/Makefile
 | 
			
		||||
index d3072d3..17ca0be 100644
 | 
			
		||||
--- a/Makefile
 | 
			
		||||
+++ b/Makefile
 | 
			
		||||
@@ -24,7 +24,7 @@ aerc: $(GOSRC)
 | 
			
		||||
 		-o $@
 | 
			
		||||
 
 | 
			
		||||
 aerc.conf: config/aerc.conf.in
 | 
			
		||||
-	sed -e 's:@SHAREDIR@:$(SHAREDIR):g' > $@ < config/aerc.conf.in
 | 
			
		||||
+	cat config/aerc.conf.in > $@
 | 
			
		||||
 
 | 
			
		||||
 DOCS := \
 | 
			
		||||
 	aerc.1 \
 | 
			
		||||
diff --git a/config/config.go b/config/config.go
 | 
			
		||||
index bfcbecf..2f4e703 100644
 | 
			
		||||
--- a/config/config.go
 | 
			
		||||
+++ b/config/config.go
 | 
			
		||||
@@ -377,6 +377,9 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) {
 | 
			
		||||
 	if err = config.LoadConfig(file); err != nil {
 | 
			
		||||
 		return nil, err
 | 
			
		||||
 	}
 | 
			
		||||
+	for i, filter := range config.Filters {
 | 
			
		||||
+		config.Filters[i].Command = strings.ReplaceAll(filter.Command, "@SHAREDIR@", sharedir)
 | 
			
		||||
+	}
 | 
			
		||||
 	if ui, err := file.GetSection("general"); err == nil {
 | 
			
		||||
 		if err := ui.MapTo(&config.General); err != nil {
 | 
			
		||||
 			return nil, err
 | 
			
		||||
-- 
 | 
			
		||||
2.23.0
 | 
			
		||||
 | 
			
		||||
@ -18,13 +18,13 @@
 | 
			
		||||
 | 
			
		||||
mkDerivation rec {
 | 
			
		||||
  pname = "nextcloud-client";
 | 
			
		||||
  version = "2.6.0";
 | 
			
		||||
  version = "2.6.1";
 | 
			
		||||
 | 
			
		||||
  src = fetchFromGitHub {
 | 
			
		||||
    owner = "nextcloud";
 | 
			
		||||
    repo = "desktop";
 | 
			
		||||
    rev = "v${version}";
 | 
			
		||||
    sha256 = "1cggk8yfy6lak48nfh691ad5y3bap49cfa2krp7vak108krgvkxi";
 | 
			
		||||
    sha256 = "18318j488pxksf4zc6zag8pdpyaks55yivn91nx3x458ax6albkz";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  patches = [
 | 
			
		||||
 | 
			
		||||
@ -10,13 +10,13 @@ with lib;
 | 
			
		||||
 | 
			
		||||
mkDerivation rec {
 | 
			
		||||
  pname = "qbittorrent";
 | 
			
		||||
  version = "4.1.8";
 | 
			
		||||
  version = "4.1.9.1";
 | 
			
		||||
 | 
			
		||||
  src = fetchFromGitHub {
 | 
			
		||||
    owner = "qbittorrent";
 | 
			
		||||
    repo = "qbittorrent";
 | 
			
		||||
    rev = "release-${version}";
 | 
			
		||||
    sha256 = "1mx59mazfmd5yaqdgb6cm8hr5sbp2xgzz3y3yipq1fwq85dj3r5w";
 | 
			
		||||
    sha256 = "19zgqlby7i1kr20wa4zd99qzd062a879xxxbmlf40rnqiqy4bhyi";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  # NOTE: 2018-05-31: CMake is working but it is not officially supported
 | 
			
		||||
 | 
			
		||||
@ -61,7 +61,7 @@ python3Packages.buildPythonApplication rec {
 | 
			
		||||
  '';
 | 
			
		||||
 | 
			
		||||
  propagatedBuildInputs = with python3Packages; [
 | 
			
		||||
    paperwork-backend pypillowfight gtk3 cairo pyxdg dateutil setuptools
 | 
			
		||||
    paperwork-backend pypillowfight gtk3 cairo pyxdg dateutil setuptools pandas
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  makeWrapperArgs = [
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@ let
 | 
			
		||||
 | 
			
		||||
in mkDerivation rec {
 | 
			
		||||
  pname = "sdrangel";
 | 
			
		||||
  version = "4.11.7";
 | 
			
		||||
  version = "4.11.12";
 | 
			
		||||
 | 
			
		||||
  src = fetchFromGitHub {
 | 
			
		||||
    owner = "f4exb";
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										42
									
								
								pkgs/applications/science/biology/deeptools/default.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								pkgs/applications/science/biology/deeptools/default.nix
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
			
		||||
{ lib
 | 
			
		||||
, python
 | 
			
		||||
}:
 | 
			
		||||
with python.pkgs;
 | 
			
		||||
buildPythonApplication rec {
 | 
			
		||||
  pname = "deepTools";
 | 
			
		||||
  version = "3.3.1";
 | 
			
		||||
 | 
			
		||||
  src = fetchPypi {
 | 
			
		||||
    inherit pname version;
 | 
			
		||||
    sha256 = "08p36p9ncj5s8qf1r7h83x4rnmi63l3yk6mnr3wgpg2qgvwl0hji";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  propagatedBuildInputs = [
 | 
			
		||||
    numpy
 | 
			
		||||
    numpydoc
 | 
			
		||||
    scipy
 | 
			
		||||
    py2bit
 | 
			
		||||
    pybigwig
 | 
			
		||||
    pysam
 | 
			
		||||
    matplotlib
 | 
			
		||||
    plotly
 | 
			
		||||
    deeptoolsintervals
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  checkInputs = [ pytest ];
 | 
			
		||||
 | 
			
		||||
  meta = with lib; {
 | 
			
		||||
    homepage = "https://deeptools.readthedocs.io/en/develop";
 | 
			
		||||
    description = "Tools for exploring deep DNA sequencing data";
 | 
			
		||||
    longDescription = ''
 | 
			
		||||
      deepTools contains useful modules to process the mapped reads data for multiple
 | 
			
		||||
      quality checks, creating normalized coverage files in standard bedGraph and bigWig
 | 
			
		||||
      file formats, that allow comparison between different files (for example, treatment and control).
 | 
			
		||||
      Finally, using such normalized and standardized files, deepTools can create many
 | 
			
		||||
      publication-ready visualizations to identify enrichments and for functional
 | 
			
		||||
      annotations of the genome.
 | 
			
		||||
    '';
 | 
			
		||||
    license = licenses.gpl3;
 | 
			
		||||
    maintainers = with maintainers; [ scalavision ];
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
@ -5,13 +5,13 @@
 | 
			
		||||
 | 
			
		||||
stdenv.mkDerivation rec {
 | 
			
		||||
  pname = "yacas";
 | 
			
		||||
  version = "1.6.1";
 | 
			
		||||
  version = "1.8.0";
 | 
			
		||||
 | 
			
		||||
  src = fetchFromGitHub {
 | 
			
		||||
    owner = "grzegorzmazur";
 | 
			
		||||
    repo = "yacas";
 | 
			
		||||
    rev = "v${version}";
 | 
			
		||||
    sha256 = "0awvlvf607r4hwl1vkhs6jq2s6ig46c66pmr4vspj2cdnypx99cc";
 | 
			
		||||
    sha256 = "0fwd98dwq6g0md3yhgyl30i377593b8rw6gsvffzvs11g3aqf1ga";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  hardeningDisable = [ "format" ];
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
{ stdenv, fetchurl, lib, qtbase, qtmultimedia, qtscript, qtsensors, qtwebkit, openssl, xkeyboard_config, wrapQtAppsHook }:
 | 
			
		||||
{ stdenv, fetchurl, lib, qtbase, qtmultimedia, qtscript, qtsensors, qtwebkit, openssl_1_0_2, xkeyboard_config, wrapQtAppsHook }:
 | 
			
		||||
 | 
			
		||||
stdenv.mkDerivation rec {
 | 
			
		||||
  pname = "p4v";
 | 
			
		||||
@ -19,7 +19,7 @@ stdenv.mkDerivation rec {
 | 
			
		||||
      qtscript
 | 
			
		||||
      qtsensors
 | 
			
		||||
      qtwebkit
 | 
			
		||||
      openssl
 | 
			
		||||
      openssl_1_0_2
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  dontWrapQtApps = true;
 | 
			
		||||
@ -43,6 +43,6 @@ stdenv.mkDerivation rec {
 | 
			
		||||
    homepage = https://www.perforce.com;
 | 
			
		||||
    license = stdenv.lib.licenses.unfreeRedistributable;
 | 
			
		||||
    platforms = [ "x86_64-linux" ];
 | 
			
		||||
    maintainers = [ stdenv.lib.maintainers.nioncode ];
 | 
			
		||||
    maintainers = with stdenv.lib.maintainers; [ nathyong nioncode ];
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@
 | 
			
		||||
, srht, redis, celery, pyyaml, markdown }:
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
  version = "0.47.9";
 | 
			
		||||
  version = "0.48.0";
 | 
			
		||||
 | 
			
		||||
  buildWorker = src: buildGoModule {
 | 
			
		||||
    inherit src version;
 | 
			
		||||
@ -20,7 +20,7 @@ in buildPythonPackage rec {
 | 
			
		||||
  src = fetchgit {
 | 
			
		||||
    url = "https://git.sr.ht/~sircmpwn/builds.sr.ht";
 | 
			
		||||
    rev = version;
 | 
			
		||||
    sha256 = "1zgaba58svhksxb1pzz8bym9p0pm7fnxsj5k6jz86095xmfijp34";
 | 
			
		||||
    sha256 = "1z5bxsn67cqffixqsrnska86mw0a6494650wbi6dbp10z03870bs";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  patches = [
 | 
			
		||||
 | 
			
		||||
@ -1,17 +1,18 @@
 | 
			
		||||
{ stdenv, fetchgit, fetchNodeModules, buildPythonPackage
 | 
			
		||||
, pgpy, flask, bleach, misaka, humanize, markdown, psycopg2, pygments, requests
 | 
			
		||||
, sqlalchemy, flask_login, beautifulsoup4, sqlalchemy-utils, celery, alembic
 | 
			
		||||
, importlib-metadata
 | 
			
		||||
, sassc, nodejs
 | 
			
		||||
, writeText }:
 | 
			
		||||
 | 
			
		||||
buildPythonPackage rec {
 | 
			
		||||
  pname = "srht";
 | 
			
		||||
  version = "0.54.3";
 | 
			
		||||
  version = "0.54.4";
 | 
			
		||||
 | 
			
		||||
  src = fetchgit {
 | 
			
		||||
    url = "https://git.sr.ht/~sircmpwn/core.sr.ht";
 | 
			
		||||
    rev = version;
 | 
			
		||||
    sha256 = "1f4srhp5g6652anifs1vyijzi2v23l2rnfpf3x96j9r8rdap42rq";
 | 
			
		||||
    sha256 = "0flxvn178hqd8ljz89ddis80zfnmzgimv4506w4dg2flbwzywy7z";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  node_modules = fetchNodeModules {
 | 
			
		||||
@ -47,6 +48,7 @@ buildPythonPackage rec {
 | 
			
		||||
    # Unofficial runtime dependencies?
 | 
			
		||||
    celery
 | 
			
		||||
    alembic
 | 
			
		||||
    importlib-metadata
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  PKGVER = version;
 | 
			
		||||
 | 
			
		||||
@ -4,12 +4,12 @@
 | 
			
		||||
 | 
			
		||||
buildPythonPackage rec {
 | 
			
		||||
  pname = "dispatchsrht";
 | 
			
		||||
  version = "0.11.1";
 | 
			
		||||
  version = "0.12.3";
 | 
			
		||||
 | 
			
		||||
  src = fetchgit {
 | 
			
		||||
    url = "https://git.sr.ht/~sircmpwn/dispatch.sr.ht";
 | 
			
		||||
    rev = version;
 | 
			
		||||
    sha256 = "1bi7vn0yr326mf2c63f2fahdlrx2c6a8d6p6bzy2ym2835qfcc0v";
 | 
			
		||||
    sha256 = "0lpc8jpyz1rg3g98546wlhr27b15g32lds77hl42aixv5f5b8lc9";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  patches = [
 | 
			
		||||
 | 
			
		||||
@ -4,11 +4,19 @@
 | 
			
		||||
, srht, pygit2, scmsrht }:
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
  version = "0.34.2";
 | 
			
		||||
  version = "0.35.6";
 | 
			
		||||
 | 
			
		||||
  buildShell = src: buildGoModule {
 | 
			
		||||
    inherit src version;
 | 
			
		||||
    pname = "git-srht-shell";
 | 
			
		||||
    goPackagePath = "git.sr.ht/~sircmpwn/git.sr.ht/gitsrht-shell";
 | 
			
		||||
 | 
			
		||||
    modSha256 = "1v4npijqgv09ssrxf1y1b3syb2fs7smy7k9rcj3ynsfrn9xgfd9y";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  buildDispatcher = src: buildGoModule {
 | 
			
		||||
    inherit src version;
 | 
			
		||||
    pname = "git-sr-ht-dispatcher";
 | 
			
		||||
    pname = "git-srht-dispatcher";
 | 
			
		||||
    goPackagePath = "git.sr.ht/~sircmpwn/git.sr.ht/gitsrht-dispatch";
 | 
			
		||||
 | 
			
		||||
    modSha256 = "1lmgmlin460g09dph2hw6yz25d4agqwjhrjv0qqsis7df9qpf3i1";
 | 
			
		||||
@ -20,7 +28,7 @@ in buildPythonPackage rec {
 | 
			
		||||
  src = fetchgit {
 | 
			
		||||
    url = "https://git.sr.ht/~sircmpwn/git.sr.ht";
 | 
			
		||||
    rev = version;
 | 
			
		||||
    sha256 = "1z10r2d9x71n1n36g55j4cswh0dqnzmgj2qiy1h92wwgq8azpiyy";
 | 
			
		||||
    sha256 = "0j8caqbzdqkgc1bdhzz4k5hgh8lhsghfgwf46d19ryf83d8ggxqc";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  patches = [
 | 
			
		||||
@ -42,6 +50,7 @@ in buildPythonPackage rec {
 | 
			
		||||
 | 
			
		||||
  postInstall = ''
 | 
			
		||||
    mkdir -p $out/bin
 | 
			
		||||
    cp ${buildShell "${src}/gitsrht-shell"}/bin/gitsrht-shell $out/bin/gitsrht-shell
 | 
			
		||||
    cp ${buildDispatcher "${src}/gitsrht-dispatch"}/bin/gitsrht-dispatch $out/bin/gitsrht-dispatch
 | 
			
		||||
  '';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,12 +4,12 @@
 | 
			
		||||
 | 
			
		||||
buildPythonPackage rec {
 | 
			
		||||
  pname = "hgsrht";
 | 
			
		||||
  version = "0.16.0";
 | 
			
		||||
  version = "0.16.2";
 | 
			
		||||
 | 
			
		||||
  src = fetchhg {
 | 
			
		||||
    url = "https://hg.sr.ht/~sircmpwn/hg.sr.ht";
 | 
			
		||||
    rev = version;
 | 
			
		||||
    sha256 = "0ncrj1cbls9ix2ig3qqwbzs6q6cmpqy3zs21p9fw3idfw703j3g0";
 | 
			
		||||
    sha256 = "02bzy31zplnlqg8rcls5n65q1h920lhy6f51w89w1kskdw7r2mhy";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  patches = [
 | 
			
		||||
 | 
			
		||||
@ -5,12 +5,12 @@
 | 
			
		||||
 | 
			
		||||
buildPythonPackage rec {
 | 
			
		||||
  pname = "metasrht";
 | 
			
		||||
  version = "0.35.3";
 | 
			
		||||
  version = "0.37.0";
 | 
			
		||||
 | 
			
		||||
  src = fetchgit {
 | 
			
		||||
    url = "https://git.sr.ht/~sircmpwn/meta.sr.ht";
 | 
			
		||||
    rev = version;
 | 
			
		||||
    sha256 = "1kcmlmdk9v59fr3r0g2q2gfkb735xza0wni9s942wh418dr66x2f";
 | 
			
		||||
    sha256 = "1jf3h2v27cbam8bwiw3x35319pzp0r651p8mfhw150jvskyvmkmr";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  nativeBuildInputs = srht.nativeBuildInputs;
 | 
			
		||||
 | 
			
		||||
@ -4,12 +4,12 @@
 | 
			
		||||
 | 
			
		||||
buildPythonPackage rec {
 | 
			
		||||
  pname = "pastesrht";
 | 
			
		||||
  version = "0.7.1";
 | 
			
		||||
  version = "0.7.3";
 | 
			
		||||
 | 
			
		||||
  src = fetchgit {
 | 
			
		||||
    url = "https://git.sr.ht/~sircmpwn/paste.sr.ht";
 | 
			
		||||
    rev = version;
 | 
			
		||||
    sha256 = "19y9ghhi4llyg7kd3a888gbjc698vdamin4hb8dk1j6pd2f0qmjp";
 | 
			
		||||
    sha256 = "15689gk37djcwdjb636d97k0il2zpdpksb95l9l4d43wipd7x5qi";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  patches = [
 | 
			
		||||
 | 
			
		||||
@ -4,12 +4,12 @@
 | 
			
		||||
 | 
			
		||||
buildPythonPackage rec {
 | 
			
		||||
  pname = "scmsrht";
 | 
			
		||||
  version = "0.15.3";
 | 
			
		||||
  version = "0.16.0";
 | 
			
		||||
 | 
			
		||||
  src = fetchgit {
 | 
			
		||||
    url = "https://git.sr.ht/~sircmpwn/scm.sr.ht";
 | 
			
		||||
    rev = version;
 | 
			
		||||
    sha256 = "1rzm3r280211w51sjngm5a3pdlzg07c64324k99bqs1fkc2yrfy6";
 | 
			
		||||
    sha256 = "0jny8ihn49n7bpw5nhdrfha78yzpxp277l50y1lj142r59kwmh22";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  nativeBuildInputs = srht.nativeBuildInputs;
 | 
			
		||||
 | 
			
		||||
@ -5,12 +5,12 @@
 | 
			
		||||
 | 
			
		||||
buildPythonPackage rec {
 | 
			
		||||
  pname = "todosrht";
 | 
			
		||||
  version = "0.51.11";
 | 
			
		||||
  version = "0.51.13";
 | 
			
		||||
 | 
			
		||||
  src = fetchgit {
 | 
			
		||||
    url = "https://git.sr.ht/~sircmpwn/todo.sr.ht";
 | 
			
		||||
    rev = version;
 | 
			
		||||
    sha256 = "0x4aray1dappalmn2f4wqrhpa5k1idccnafbfhsnfi6nj718i33a";
 | 
			
		||||
    sha256 = "19gywq5j7wlpk7j2whm2ivz0z0i3j50n7k7bx29pghndl7l43c18";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  patches = [
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,12 @@
 | 
			
		||||
{ lib, mkDerivation, fetchurl, qmake, qtscript }:
 | 
			
		||||
 | 
			
		||||
mkDerivation rec {
 | 
			
		||||
  name = "smplayer-19.5.0";
 | 
			
		||||
  pname = "smplayer";
 | 
			
		||||
  version = "19.10.0";
 | 
			
		||||
 | 
			
		||||
  src = fetchurl {
 | 
			
		||||
    url = "mirror://sourceforge/smplayer/${name}.tar.bz2";
 | 
			
		||||
    sha256 = "1xda9pbrc3dfbs71n5l8yszlcywz9456mwkv52vmn8lszhvjpjxm";
 | 
			
		||||
    url = "mirror://sourceforge/${pname}/${pname}-${version}.tar.bz2";
 | 
			
		||||
    sha256 = "0sq7hr10b4pbbi0y1q4mxs24h2lb042nv4rqr03r72bp57353xsl";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  buildInputs = [ qtscript ];
 | 
			
		||||
@ -13,13 +14,12 @@ mkDerivation rec {
 | 
			
		||||
 | 
			
		||||
  dontUseQmakeConfigure = true;
 | 
			
		||||
 | 
			
		||||
  preConfigure = ''
 | 
			
		||||
    makeFlags="PREFIX=$out"
 | 
			
		||||
  '';
 | 
			
		||||
  makeFlags = [ "PREFIX=${placeholder "out"}" ];
 | 
			
		||||
 | 
			
		||||
  meta = {
 | 
			
		||||
    description = "A complete front-end for MPlayer";
 | 
			
		||||
    homepage = http://smplayer.sourceforge.net/;
 | 
			
		||||
    longDescription = "Either mplayer or mpv should also be installed for smplayer to play medias";
 | 
			
		||||
    homepage = https://www.smplayer.info;
 | 
			
		||||
    license = lib.licenses.gpl3Plus;
 | 
			
		||||
    platforms = lib.platforms.linux;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@ -1,26 +1,22 @@
 | 
			
		||||
{ stdenv, fetchFromGitHub, makeWrapper, nx-libs, xorg }:
 | 
			
		||||
{ stdenv, fetchFromGitHub, makeWrapper, nx-libs, xorg, getopt, gnugrep, gawk, ps, mount, iproute }:
 | 
			
		||||
stdenv.mkDerivation rec {
 | 
			
		||||
  pname = "x11docker";
 | 
			
		||||
  version = "6.2.0";
 | 
			
		||||
  version = "6.3.0";
 | 
			
		||||
  src = fetchFromGitHub {
 | 
			
		||||
    owner = "mviereck";
 | 
			
		||||
    repo = "x11docker";
 | 
			
		||||
    rev = "v${version}";
 | 
			
		||||
    sha256 = "19q5vrhspxpjkdhhlgya2sa2fgjg8gyd3kmnb83nlfs46p8jx4f4";
 | 
			
		||||
    sha256 = "0x2sx41y3ylzg511x52k3wh8mfbzp4ialpas6sn4ccagqxh2hc4y";
 | 
			
		||||
  };
 | 
			
		||||
  nativeBuildInputs = [ makeWrapper ];
 | 
			
		||||
  buildInputs = [ nx-libs xorg.xhost xorg.xinit ];
 | 
			
		||||
 | 
			
		||||
  dontBuild = true;
 | 
			
		||||
 | 
			
		||||
  PATH_PREFIX = "${nx-libs}/bin:${xorg.xdpyinfo}/bin:${xorg.xhost}/bin:${xorg.xinit}/bin";
 | 
			
		||||
 | 
			
		||||
  # Don't install `x11docker-gui`, because requires `kaptain` dependency
 | 
			
		||||
  installPhase = ''
 | 
			
		||||
    install -D x11docker "$out/bin/x11docker";
 | 
			
		||||
    #install -D x11docker-gui "$out/bin/x11docker-gui";
 | 
			
		||||
    wrapProgram "$out/bin/x11docker" --prefix PATH : "${PATH_PREFIX}"
 | 
			
		||||
    #wrapProgram "$out/bin/x11docker-gui" --prefix PATH : "${PATH_PREFIX}"
 | 
			
		||||
    # GUI disabled because of missing `kaptain` dependency
 | 
			
		||||
    wrapProgram "$out/bin/x11docker" \
 | 
			
		||||
      --prefix PATH : "${stdenv.lib.makeBinPath [ getopt gnugrep gawk ps mount iproute nx-libs xorg.xdpyinfo xorg.xhost xorg.xinit ]}"
 | 
			
		||||
  '';
 | 
			
		||||
 | 
			
		||||
  meta = {
 | 
			
		||||
@ -28,5 +24,6 @@ stdenv.mkDerivation rec {
 | 
			
		||||
    homepage = https://github.com/mviereck/x11docker;
 | 
			
		||||
    license = stdenv.lib.licenses.mit;
 | 
			
		||||
    maintainers = with stdenv.lib.maintainers; [ jD91mZM2 ];
 | 
			
		||||
    platforms = stdenv.lib.platforms.linux;
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,29 +0,0 @@
 | 
			
		||||
{ python3
 | 
			
		||||
, fetchFromGitHub
 | 
			
		||||
, stdenv
 | 
			
		||||
}:
 | 
			
		||||
 | 
			
		||||
python3.pkgs.buildPythonApplication rec {
 | 
			
		||||
  pname = "nix-prefetch-github";
 | 
			
		||||
  version = "2.3.1";
 | 
			
		||||
 | 
			
		||||
  src = fetchFromGitHub {
 | 
			
		||||
    owner = "seppeljordan";
 | 
			
		||||
    repo = "nix-prefetch-github";
 | 
			
		||||
    rev = "v${version}";
 | 
			
		||||
    sha256 = "13wvq13iiva97a16kahfpxar5ppb015nnbn7d4v9s9jyxdickc2c";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  propagatedBuildInputs = with python3.pkgs; [
 | 
			
		||||
    attrs
 | 
			
		||||
    click
 | 
			
		||||
    effect
 | 
			
		||||
    jinja2
 | 
			
		||||
  ];
 | 
			
		||||
  meta = with stdenv.lib; {
 | 
			
		||||
    description = "Prefetch sources from github";
 | 
			
		||||
    homepage = https://github.com/seppeljordan/nix-prefetch-github;
 | 
			
		||||
    license = licenses.gpl3;
 | 
			
		||||
    maintainers = [ maintainers.seppeljordan ];
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
{ stdenv, fetchFromGitHub, fontforge, pythonPackages, python }:
 | 
			
		||||
{ stdenv, fetchFromGitHub, fontforge, python3Packages, python3 }:
 | 
			
		||||
 | 
			
		||||
stdenv.mkDerivation rec {
 | 
			
		||||
  pname = "liberation-sans-narrow";
 | 
			
		||||
@ -11,7 +11,7 @@ stdenv.mkDerivation rec {
 | 
			
		||||
    sha256 = "1qw554jbdnqkg6pjjl4cqkgsalq3398kzvww2naw30vykcz752bm";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  buildInputs = [ fontforge pythonPackages.fonttools python ];
 | 
			
		||||
  buildInputs = [ fontforge python3Packages.fonttools python3 ];
 | 
			
		||||
 | 
			
		||||
  installPhase = ''
 | 
			
		||||
    find . -name '*Narrow*.ttf' -exec install -m444 -Dt $out/share/fonts/truetype {} \;
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
{ stdenv, fetchgit, fontforge, pythonFull }:
 | 
			
		||||
{ stdenv, fetchgit, fontforge, python3 }:
 | 
			
		||||
 | 
			
		||||
stdenv.mkDerivation rec {
 | 
			
		||||
  pname = "rictydiminished-with-firacode";
 | 
			
		||||
@ -27,7 +27,7 @@ stdenv.mkDerivation rec {
 | 
			
		||||
 | 
			
		||||
  nativeBuildInputs = [
 | 
			
		||||
    fontforge
 | 
			
		||||
    (pythonFull.withPackages (ps: [
 | 
			
		||||
    (python3.withPackages (ps: [
 | 
			
		||||
      ps.jinja2
 | 
			
		||||
      ps.py3to2
 | 
			
		||||
      ps.fonttools
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										35
									
								
								pkgs/data/themes/lounge/default.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								pkgs/data/themes/lounge/default.nix
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
			
		||||
{ stdenv, fetchFromGitHub, meson, ninja, sassc, gtk3, gnome3, gdk-pixbuf, librsvg, gtk-engine-murrine }:
 | 
			
		||||
 | 
			
		||||
stdenv.mkDerivation rec {
 | 
			
		||||
  pname = "lounge-gtk-theme";
 | 
			
		||||
  version = "1.22";
 | 
			
		||||
 | 
			
		||||
  src = fetchFromGitHub {
 | 
			
		||||
    owner = "monday15";
 | 
			
		||||
    repo = pname;
 | 
			
		||||
    rev = version;
 | 
			
		||||
    sha256 = "1y1wkfsv2zrxqcqr53lmr9743mvzcy4swi5j6sxmk1aykx6ccs1p";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  nativeBuildInputs = [ meson ninja sassc gtk3 ];
 | 
			
		||||
 | 
			
		||||
  buildInputs = [ gdk-pixbuf librsvg ];
 | 
			
		||||
 | 
			
		||||
  propagatedUserEnvPkgs = [ gtk-engine-murrine ];
 | 
			
		||||
 | 
			
		||||
  mesonFlags = [
 | 
			
		||||
    "-D gnome_version=${stdenv.lib.versions.majorMinor gnome3.gnome-shell.version}"
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  postFixup = ''
 | 
			
		||||
    gtk-update-icon-cache "$out"/share/icons/Lounge-aux;
 | 
			
		||||
  '';
 | 
			
		||||
 | 
			
		||||
  meta = with stdenv.lib; {
 | 
			
		||||
    description = "Simple and clean GTK theme with vintage scrollbars, inspired by Absolute, based on Adwaita";
 | 
			
		||||
    homepage = https://github.com/monday15/lounge-gtk-theme;
 | 
			
		||||
    license = licenses.gpl3Plus;
 | 
			
		||||
    platforms = platforms.unix;
 | 
			
		||||
    maintainers = [ maintainers.romildo ];
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
@ -3,13 +3,13 @@
 | 
			
		||||
 | 
			
		||||
stdenv.mkDerivation rec {
 | 
			
		||||
  pname = "yaru";
 | 
			
		||||
  version = "19.10.2";
 | 
			
		||||
  version = "19.10.4";
 | 
			
		||||
 | 
			
		||||
  src = fetchFromGitHub {
 | 
			
		||||
    owner = "ubuntu";
 | 
			
		||||
    repo = "yaru";
 | 
			
		||||
    rev = version;
 | 
			
		||||
    sha256 = "1azyn8pr0kpbq4wlz91f5amqyxqq0x2mxkglzl488sf39fl0gnbj";
 | 
			
		||||
    sha256 = "1dj6awlz13787783ds9mdid75rd4vvgpg52h6x19pxdga3k17s9b";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  nativeBuildInputs = [ meson sassc pkg-config glib ninja python3 ];
 | 
			
		||||
 | 
			
		||||
@ -1,19 +1,20 @@
 | 
			
		||||
{ stdenv, fetchurl, meson, ninja, pkgconfig, SDL, SDL2, alsaLib, avahi, bullet, check, curl, dbus,
 | 
			
		||||
  doxygen, expat, fontconfig, freetype, fribidi, ghostscript, giflib,
 | 
			
		||||
  glib, gst_all_1, gtk3, harfbuzz, ibus, jbig2dec, libGL, libdrm, libinput,
 | 
			
		||||
  libjpeg, libpng, libpulseaudio, libraw, librsvg, libsndfile,
 | 
			
		||||
  libspectre, libtiff, libwebp, libxkbcommon, luajit, lz4, mesa,
 | 
			
		||||
  openjpeg, openssl, poppler, python27Packages, systemd, udev,
 | 
			
		||||
  utillinux, writeText, xorg, zlib
 | 
			
		||||
{ stdenv, fetchurl, meson, ninja, pkgconfig, SDL, SDL2, alsaLib,
 | 
			
		||||
  avahi, bullet, check, curl, dbus, doxygen, expat, fontconfig,
 | 
			
		||||
  freetype, fribidi, ghostscript, giflib, glib, gst_all_1, gtk3,
 | 
			
		||||
  harfbuzz, ibus, jbig2dec, libGL, libdrm, libinput, libjpeg, libpng,
 | 
			
		||||
  libpulseaudio, libraw, librsvg, libsndfile, libspectre, libtiff,
 | 
			
		||||
  libwebp, libxkbcommon, luajit, lz4, mesa, openjpeg, openssl,
 | 
			
		||||
  poppler, python27Packages, systemd, udev, utillinux, writeText,
 | 
			
		||||
  xorg, zlib
 | 
			
		||||
}:
 | 
			
		||||
 | 
			
		||||
stdenv.mkDerivation rec {
 | 
			
		||||
  pname = "efl";
 | 
			
		||||
  version = "1.23.1";
 | 
			
		||||
  version = "1.23.2";
 | 
			
		||||
 | 
			
		||||
  src = fetchurl {
 | 
			
		||||
    url = "http://download.enlightenment.org/rel/libs/${pname}/${pname}-${version}.tar.xz";
 | 
			
		||||
    sha256 = "0q9g4j7k10s1a8rv2ca9v9lydh7ml3zsrqvgncc4qhvdl76208nn";
 | 
			
		||||
    sha256 = "14yljnnmb89s8j6ip08ip5d01zkgzbzr1h4fr4bwk9lh8r59x3ds";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  nativeBuildInputs = [
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,6 @@ lib.makeScope pkgs.newScope (self: with self; {
 | 
			
		||||
  libsoup = pkgs.libsoup.override { gnomeSupport = true; };
 | 
			
		||||
  libchamplain = pkgs.libchamplain.override { libsoup = libsoup; };
 | 
			
		||||
  gnome3 = self // { recurseForDerivations = false; };
 | 
			
		||||
  gegl_0_4 = pkgs.gegl_0_4.override { gtk = pkgs.gtk3; };
 | 
			
		||||
 | 
			
		||||
# ISO installer
 | 
			
		||||
# installerIso = callPackage ./installer.nix {};
 | 
			
		||||
@ -359,4 +358,6 @@ lib.makeScope pkgs.newScope (self: with self; {
 | 
			
		||||
  nautilus-sendto = throw "deprecated 2019-09-17: abandoned";
 | 
			
		||||
 | 
			
		||||
  inherit (pkgs) vala; # added 2019-10-10
 | 
			
		||||
 | 
			
		||||
  inherit (pkgs) gegl_0_4; # added 2019-10-31
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,22 @@
 | 
			
		||||
{ stdenv, fetchurl, meson, ninja, pkgconfig, gnome3, gtk3, wrapGAppsHook
 | 
			
		||||
, glib, amtk, appstream-glib, gobject-introspection, python3
 | 
			
		||||
, webkitgtk, gettext, itstool, gsettings-desktop-schemas }:
 | 
			
		||||
{ stdenv
 | 
			
		||||
, fetchurl
 | 
			
		||||
, meson
 | 
			
		||||
, ninja
 | 
			
		||||
, pkgconfig
 | 
			
		||||
, gnome3
 | 
			
		||||
, gtk3
 | 
			
		||||
, wrapGAppsHook
 | 
			
		||||
, glib
 | 
			
		||||
, amtk
 | 
			
		||||
, appstream-glib
 | 
			
		||||
, gobject-introspection
 | 
			
		||||
, python3
 | 
			
		||||
, webkitgtk
 | 
			
		||||
, gettext
 | 
			
		||||
, itstool
 | 
			
		||||
, gsettings-desktop-schemas
 | 
			
		||||
, shared-mime-info
 | 
			
		||||
}:
 | 
			
		||||
 | 
			
		||||
stdenv.mkDerivation rec {
 | 
			
		||||
  pname = "devhelp";
 | 
			
		||||
@ -11,10 +27,25 @@ stdenv.mkDerivation rec {
 | 
			
		||||
    sha256 = "0zpmn6fgkgiayvn4diia5df0s6s7dqrdnp3nrvpavsmgn0vhb4pg";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  nativeBuildInputs = [ meson ninja pkgconfig gettext itstool wrapGAppsHook appstream-glib gobject-introspection python3 ];
 | 
			
		||||
  nativeBuildInputs = [
 | 
			
		||||
    meson
 | 
			
		||||
    ninja
 | 
			
		||||
    pkgconfig
 | 
			
		||||
    gettext
 | 
			
		||||
    itstool
 | 
			
		||||
    wrapGAppsHook
 | 
			
		||||
    appstream-glib
 | 
			
		||||
    gobject-introspection
 | 
			
		||||
    python3
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  buildInputs = [
 | 
			
		||||
    glib gtk3 webkitgtk amtk
 | 
			
		||||
    gnome3.adwaita-icon-theme gsettings-desktop-schemas
 | 
			
		||||
    glib
 | 
			
		||||
    gtk3
 | 
			
		||||
    webkitgtk
 | 
			
		||||
    amtk
 | 
			
		||||
    gnome3.adwaita-icon-theme
 | 
			
		||||
    gsettings-desktop-schemas
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  doCheck = true;
 | 
			
		||||
@ -24,6 +55,14 @@ stdenv.mkDerivation rec {
 | 
			
		||||
    patchShebangs meson_post_install.py
 | 
			
		||||
  '';
 | 
			
		||||
 | 
			
		||||
  preFixup = ''
 | 
			
		||||
    gappsWrapperArgs+=(
 | 
			
		||||
      # Fix pages being blank
 | 
			
		||||
      # https://gitlab.gnome.org/GNOME/devhelp/issues/14
 | 
			
		||||
      --prefix XDG_DATA_DIRS : "${shared-mime-info}/share"
 | 
			
		||||
    )
 | 
			
		||||
  '';
 | 
			
		||||
 | 
			
		||||
  passthru = {
 | 
			
		||||
    updateScript = gnome3.updateScript {
 | 
			
		||||
      packageName = "devhelp";
 | 
			
		||||
@ -33,8 +72,8 @@ stdenv.mkDerivation rec {
 | 
			
		||||
 | 
			
		||||
  meta = with stdenv.lib; {
 | 
			
		||||
    description = "API documentation browser for GNOME";
 | 
			
		||||
    homepage = https://wiki.gnome.org/Apps/Devhelp;
 | 
			
		||||
    license = licenses.gpl2;
 | 
			
		||||
    homepage = "https://wiki.gnome.org/Apps/Devhelp";
 | 
			
		||||
    license = licenses.gpl3Plus;
 | 
			
		||||
    maintainers = gnome3.maintainers;
 | 
			
		||||
    platforms = platforms.linux;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user