Merge staging-next into staging

This commit is contained in:
Frederik Rietdijk 2019-11-05 14:18:08 +01:00
commit c4e30cf98c
252 changed files with 5991 additions and 1658 deletions

View File

@ -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

View File

@ -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";

View File

@ -14,14 +14,14 @@
starting VDE switch for network 1
<prompt>&gt;</prompt>
</screen>
You can then take any Perl statement, e.g.
You can then take any Python statement, e.g.
<screen>
<prompt>&gt;</prompt> startAll
<prompt>&gt;</prompt> testScript
<prompt>&gt;</prompt> $machine->succeed("touch /tmp/foo")
<prompt>&gt;</prompt> print($machine->succeed("pwd")) # Show stdout of command
<prompt>&gt;</prompt> start_all()
<prompt>&gt;</prompt> test_script()
<prompt>&gt;</prompt> machine.succeed("touch /tmp/foo")
<prompt>&gt;</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).

View File

@ -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 didnt 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>

View File

@ -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>

View File

@ -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>

View 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))

View 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;
}

View 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 ];
};
}

View File

@ -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

View File

@ -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)

View 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 ];
};
}

View File

@ -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;
}

View File

@ -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);

View 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;
}

View File

@ -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";

View File

@ -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

View File

@ -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";

View File

@ -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>
'';
}];

View File

@ -181,6 +181,7 @@ in {
ProtectKernelModules = true;
RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX AF_NETLINK";
RestrictNamespaces = true;
Restart = "always";
};
};

View File

@ -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";

View File

@ -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

View 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 ];
}

View 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 ];
};
};
}

View 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";
};
}

View File

@ -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

View File

@ -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'"
)
'';
}

View File

@ -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 {};

View File

@ -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")
'';
})

View File

@ -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"
)
'';
})

View File

@ -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}"
)
'';
})

View 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 {

View File

@ -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")
'';
})

View File

@ -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"
)
'';
}

View 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;
{
@ -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.'"
)
'';
};
}

View File

@ -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()
'';
})

View 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
View 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;
'';
})

View File

@ -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"
)
'';
})

View File

@ -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'")
'';
})

View 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
@ -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()
'';
};

View File

@ -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")
'';
})

View File

@ -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()
'';
})

View File

@ -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
View 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'");
'';
})

View File

@ -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()
'';
})

View File

@ -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")
'';
})

View File

@ -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")
'';
})

View File

@ -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"
);
)
'';
};

View File

@ -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 ++

View File

@ -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 = [

View File

@ -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 = [

View File

@ -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"

View File

@ -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 = [

View File

@ -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; [

View File

@ -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 ];

View File

@ -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;

View File

@ -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 {

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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 = ''

View File

@ -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 ]; }

View File

@ -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;
};
}

View File

@ -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;
};
}

View File

@ -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; {

View File

@ -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 = ''

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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 = [];
};
}

View File

@ -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;

View File

@ -18,6 +18,10 @@ buildGoModule rec {
python3.pkgs.wrapPython
];
patches = [
./runtime-sharedir.patch
];
pythonPath = [
python3.pkgs.colorama
];

View File

@ -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

View File

@ -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 = [

View File

@ -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

View File

@ -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 = [

View File

@ -39,7 +39,7 @@ let
in mkDerivation rec {
pname = "sdrangel";
version = "4.11.7";
version = "4.11.12";
src = fetchFromGitHub {
owner = "f4exb";

View 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 ];
};
}

View File

@ -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" ];

View File

@ -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 ];
};
}

View File

@ -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 = [

View File

@ -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;

View File

@ -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 = [

View File

@ -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
'';

View File

@ -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 = [

View File

@ -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;

View File

@ -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 = [

View File

@ -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;

View File

@ -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 = [

View File

@ -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;
};

View File

@ -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;
};
}

View File

@ -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 ];
};
}

View File

@ -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 {} \;

View File

@ -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

View 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 ];
};
}

View File

@ -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 ];

View File

@ -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 = [

View File

@ -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
})

View File

@ -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