NixOS Planet

September 26, 2016

Sander van der Burg

Simulating NPM global package installations in Nix builds (or: building Grunt projects with the Nix package manager)

A while ago, I "rebranded" my second re-engineered version of npm2nix into node2nix and officially released it as such. My main two reasons for giving the tool a different name is that node2nix is neither a fork nor a continuation of npm2nix, but a tool that is written from scratch (though it incorporates some of npm2nix's ideas and concepts including most of its dependencies).

Furthermore, it approaches the expression generation problem in a fundamentally different way -- whereas npm2nix generates derivations for each package in a dependency tree and composes symlinks to their Nix store paths to allow a package to find its dependencies, node2nix deploys an entire dependency tree in one derivation so that it can more accurately mimic NPM's behaviour including flat-module installations (at the expense of losing the ability to share dependencies among packages and projects).

Because node2nix is conceptually different, I have decided to rename the project so that it can be used alongside the original npm2nix tool that still implements the old generation concepts.

Besides officially releasing node2nix, I have recently extended its feature set with a new concept for a recurring class of NPM development projects.

Global NPM development dependencies


As described in earlier blog posts, node2nix (as well as npm2nix) generate Nix expressions from a set of third party NPM packages (obtained from external sources, such as the NPM registry) or a development project's package.json file.

Although node2nix works fine for most of my development projects, I have noticed that for a recurring class of projects, the auto generation approach is too limited -- some NPM projects may require the presence of globally installed packages and must run additional build steps in order to be deployed properly. A prominent example of such a category of projects are Grunt projects.

Grunt advertises itself as a "The JavaScript Task Runner" and can be used to run all kinds of things, such as code generation, linting, minification etc. The tasks that Grunt carries out are implemented as plugins that must be deployed as a project's development dependencies with the NPM package manager.

(As a sidenote: it is debatable whether Grunt is a tool that NPM developers should use, as NPM itself can also carry out build steps through its script directive, but that discussion is beyond the scope of this blog post).

A Grunt workflow typically looks as follows. Consider an example project, with the following Gruntfile.js:


module.exports = function(grunt) {

grunt.initConfig({
jshint: {
files: ['Gruntfile.js', 'src/**/*.js'],
options: {
globals: {
jQuery: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
}
});

grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');

grunt.registerTask('default', ['jshint']);

};

The above Gruntfile defines a configuration that iterates over all JavaScript files (*.js files) in the src/ directory and invokes jshint to check for potential errors and code smells.

To deploy the development project, we first have to globally install the grunt-cli command-line utility:


$ npm install -g grunt-cli
$ which grunt
/usr/local/bin/grunt

To be able to carry out the steps, we must update a project's package.json file to have grunt and all its required Grunt plugins as development dependencies:


{
"name": "grunt-test",
"version": "0.0.1",
"private": "true",
"devDependencies": {
"grunt": "*",
"grunt-contrib-jshint": "*",
"grunt-contrib-watch": "*"
}
}

Then we must install the development dependencies with the NPM package manager:


$ npm install

And finally, we can run the grunt command-line utility to execute our tasks:


$ grunt

"Global dependencies" in Nix


Contrary to NPM, Nix does not support "global dependencies". As a matter of fact, it takes all kinds of precautions to prevent global dependencies to influence the builds that it performs, such as storing all packages in isolation in a so-called Nix store (e.g. /nix/store--grunt-1.0.1 as opposed to storing files in global directories, such as /usr/lib), initially clearing all environment variables (e.g. PATH) and setting these to the Nix store paths of the provided dependencies to allow packages to find them, running builds in chroot environments, etc.

These precautions are taken for a very good reason: purity -- each Nix package stored in the Nix store has a hash prefix that is computed from all involved build-time dependencies.

With pure builds, we know that (for example) if we encounter a build performed on one machine with a specific hash code and a build with an identical hash code on another machine, their build results are identical as well (with some caveats, but in general there are no observable side effects). Pure package builds are a crucial ingredient to make deployments of systems reliable and reproducible.

In Nix, we must always be explicit about the dependencies of a build. When a dependency is unspecified (something that commonly happens with global dependencies), a build will typically fail because it cannot be (implicitly) found. Similarly, when a build has dependencies on packages that would normally have to be installed globally (e.g. non-NPM dependencies), we must now explicitly provide them as a build inputs.

The problem with node2nix is that it automatically generates Nix expressions and that global dependencies cannot be detected automatically, because they are not specified anywhere in a package.json configuration file.

To cope with this limitation, the generated Nix expressions are made overridable, so that any missing dependency can be provided manually. For example, we may want to deploy an NPM package named floomatic from the following JSON file (node-packages.json):


[
"floomatic"
]

We can generate Nix expressions from the above specification, by running:


$ node2nix -i node-packages.json

One of floomatic's dependencies is an NPM package named: native-diff-match-patch that requires the Qt 4.x library and pkgconfig. These two packages are non-NPM package dependencies left undetected by the node2nix generator. In conventional Linux distributions, these packages typically reside in global directories, such as /usr/lib, and can still be implicitly found.

By creating an override expression (named: override.nix), we can inject these missing (global) dependencies ourselves:


{pkgs ? import <nixpkgs> {
inherit system;
}, system ? builtins.currentSystem}:

let
nodePackages = import ./default.nix {
inherit pkgs system;
};
in
nodePackages // {
floomatic = nodePackages.floomatic.override (oldAttrs: {
buildInputs = oldAttrs.buildInputs ++ [
pkgs.pkgconfig
pkgs.qt4
];
});
}

With the override expression shown above, we can correctly deploy the floomatic package, by running:


$ nix-build override.nix -A floomatic

Providing supplemental NPM packages to an NPM development project


Similar to non-NPM dependencies, we also need to supply the grunt-cli as an additional dependency to allow a Grunt project build to succeed in a Nix build environment. What makes this process difficult is that grunt-cli is also an NPM package. As a consequence, we need to generate a second set of Nix expressions and propagate their generated package configurations as parameters to the former expression. Although it was already possible to do this, because the Nix language is flexible enough, the process is quite complex, hacky and inconvenient.

In the latest node2nix version, I have automated this workflow -- when generating expressions for a development project, it is now also possible to provide a supplemental package specification. For example, for our trivial Grunt project, we can create the following supplemental JSON file (supplement.json) that provides the grunt-cli:


[
"grunt-cli"
]

We can generate Nix expressions for the development project and supplemental package set, by running:


$ node2nix -d -i package.json --supplement-input supplement.json

Besides providing the grunt-cli as an additional dependency, we also need to run grunt after obtaining all NPM dependencies. With the following wrapper expression (override.nix), we can run the Grunt task runner after all NPM packages have been successfully deployed:


{ pkgs ? import <nixpkgs> {}
, system ? builtins.currentSystem
}:

let
nodePackages = import ./default.nix {
inherit pkgs system;
};
in
nodePackages // {
package = nodePackages.package.override {
postInstall = "grunt";
};
}

As may be observed in the expression shown above, the postInstall hook is responsible for invoking the grunt command.

With the following command-line instruction, we can use the Nix package manager to deploy our Grunt project:


$ nix-build override.nix -A package

Conclusion


In this blog post, I have explained a recurring limitation of node2nix that makes it difficult to deploy projects having dependencies on NPM packages that (in conventional Linux distributions) are typically installed in global file system locations, such as grunt-cli. Furthermore, I have described a new node2nix feature that provides a solution to this problem.

In addition to Grunt projects, the solution described in this blog post is relevant for other tools as well, such as ESLint.

All features described in this blog post are part of the latest node2nix release (version 1.1.0) that can be obtained from the NPM registry and the Nixpkgs collection.

Besides a new release, node2nix is now also used to generate the expressions for the set of NPM packages included with the development and upcoming 16.09 versions of Nixpkgs.

by Sander van der Burg (noreply@blogger.com) at September 26, 2016 10:39 PM

September 22, 2016

Flying Circus

DevOps Autumn Sprint Halle 28th-30th September 2016

Next week our Autumn 2016 Sprint starts and we really look forward to welcome our guests. We are in the midst of preparation and hope the weather plays along. All details around the sprint can be find on Meetup. Interesting topics are on the agenda as: backy, batou, NixOS and more – there is an Etherpad to gather them.

If you want to contribute but can’t make it in person, think about join us remote. Just let us know in advance (send a short message to mail@flyingcircus.io or poke us on twitter @flyingcircusio).


by Andrea at September 22, 2016 02:37 PM

August 22, 2016

Sander van der Burg

An extended self-adaptive deployment framework for service-oriented systems


Five years ago, while I was still in academia, I built an extension framework around Disnix (named: Dynamic Disnix) that enables self-adaptive redeployment of service-oriented systems. It was an interesting application as it demonstrated the full potential of service-oriented systems having their deployment processes automated with Disnix.

Moreover, the corresponding research paper was accepted for presentation at the SEAMS 2011 symposium (co-located with ICSE 2011) in Honolulu (Hawaii), which was (obviously!) a nice place to visit. :-)

Disnix's development was progressing at a very low pace for a while after I left academia, but since the end of 2014 I made some significant improvements. In contrast to the basic toolset, I did not improve Dynamic Disnix -- apart from the addition of a port assigner tool, I only kept the implementation in sync with Disnix's API changes to prevent it from breaking.

Recently, I have used Dynamic Disnix to give a couple of demos. As a result, I have improved some of its aspects a bit. For example, some basic documentation has been added. Furthermore, I have extended the framework's architecture to take a couple of new deployment planning aspects into account.

Disnix


For readers unfamiliar with Disnix: the primary purpose of the basic Disnix toolset is executing deployment processes of service-oriented systems. Deployments are driven by three kinds of declarative specifications:

  • The services model captures the services (distributed units of deployments) of which a system consists, their build/configuration properties and their inter-dependencies (dependencies on other services that may have to be reached through a network link).
  • The infrastructure model describes the target machines where services can be deployed to and their characteristics.
  • The distribution model maps services in the services model to machines in the infrastructure model.

By writing instances of the above specifications and running disnix-env:

$ disnix-env -s services.nix -i infrastructure.nix -d distribution.nix

Disnix executes all activities to get the system deployed, such as building their services from source code, distributing them to the target machines in the network and activating them. Changing any of these models and running disnix-env again causes the system to be upgraded. In case of an upgrade, Disnix will only execute the required activities making the process more efficient than deploying a system from scratch.

"Static" Disnix


So, what makes Disnix's deployment approach static? When looking at software systems from a very abstract point of view, they are supposed to meet a collection of functional and non-functional requirements. A change in a network of machines affects the ability for a service-oriented system to meet them, as the services of which these systems consist are typically distributed.

If a system relies on a critical component that has only one instance deployed and the machine that hosts it crashes, the functional requirements can no longer be met. However, even if we have multiple instances of the same components giving better guarantees that no functional requirements will be broken, important non-functional requirements may be affected, such as the responsiveness of a system.

We may also want to optimize a system's non-functional properties, such as its responsiveness, by adding more machines to the network that offer more system resources, or by changing the configuration of existing machine, e.g. upgrading the amount available RAM.

The basic Disnix toolset is considered static, because all these events events require manual modifications to the Disnix models for redeployment, so that a system can meet its requirements under the changed conditions.

For simple systems, manual reconfiguration is still doable, but with one hundred services, one hundred machines or a high frequency of events (or a combination of the three), it becomes too complex and time consuming.

For example, when a machine has been added or removed, we must rewrite the distribution model in such a way that all services are deployed to at least one machine and that none of them are mapped to machines that are not capable or allowed to host them. Furthermore, with microservices (one of their traits is that they typically embed HTTP servers), we must typically bind them to unique TCP ports that do not conflict with system services or other services deployed by Disnix. None of these configuration aspects are trivial for large service-oriented systems.

Dynamic Disnix


Dynamic Disnix extends Disnix's architecture with additional models and tools to cope with the dynamism of service oriented-systems. In the latest version, I have extended its architecture (which has been based on the old architecture described in the SEAMS 2011 paper and corresponding blog post):


The above diagram shows the structure of the dydisnix-self-adapt tool. The ovals denote command-line utilities, the rectangles denote files and the arrows denote files as inputs or outputs. As with the basic Disnix toolset, dydisnix-self-adapt is composed of command-line utilities each being responsible for executing an individual deployment activity:

  • On the top right, the infrastructure generator is shown that captures the configurations of the machines in the network and generates an infrastructure model from it. Currently, two different kinds of generators can be used: disnix-capture-infra (included with the basic toolset) that uses a bootstrap infrastructure model with connectivity settings, or dydisnix-geninfra-avahi that uses multicast DNS (through Avahi) to retrieve the machines' properties.
  • dydisnix-augment-infra is responsible for augmenting the generated infrastructure model with additional settings, such as passwords. It is typically undesired to automatically publish privacy-sensitive settings over a network using insecure connection protocols.
  • disnix-snapshot can be optionally used to preemptively capture the state of all stateful services (services with property: deployState = true; in the services model) so that the state of these services can be restored if a machine crashes or disappears. This tool is new in the extended architecture.
  • dydisnix-gendist generates a mapping of services to machines based on technical and non-functional properties defined in the services and infrastructure models.
  • dydisnix-port-assign assigns unique TCP port numbers to previously undeployed services and retains assigned TCP ports in a previous deployment for optimization purposes. This tool is new in the extended architecture.
  • disnix-env redeploys the system with the (statically) provided services model and the dynamically generated infrastructure and distribution models.

An example usage scenario


When a system has been configured to be (statically) deployed with Disnix (such as the infamous StaffTracker example cases that come in several variants), we need to add a few additional deployment specifications to make it dynamically deployable.

Auto discovering the infrastructure model


First, we must configure the machines in such a way that they publish their own configurations. The basic toolset comes with a primitive solution called: disnix-capture-infra that does not require any additional configuration -- it consults the Disnix service that is installed on every target machine.

By providing a simple bootstrap infrastructure model (e.g. infrastructure-bootstrap.nix) that only provides connectivity settings:


{
test1.properties.hostname = "test1";
test2.properties.hostname = "test2";
}

and running disnix-capture-infra, we can obtain the machines' configuration properties:


$ disnix-capture-infra infrastructure-bootstrap.nix

By setting the following environment variable, we can configure Dynamic Disnix to use the above command to capture the machines' infrastructure properties:


$ export DYDISNIX_GENINFRA="disnix-capture-infra infrastructure-bootstrap.nix"

Alternatively, there is the Dynamic Disnix Avahi publisher that is more powerful, but at the same time much more experimental and unstable than disnix-capture-infra.

When using Avahi, each machine uses multicast DNS (mDNS) to publish their own configuration properties. As a result, no bootstrap infrastructure model is needed. Simply gathering the data published by the machines on the same subnet suffices.

When using NixOS on a target machine, the Avahi publisher can be enabled by cloning the dydisnix-avahi Git repository and adding the following lines to /etc/nixos/configuration.nix:


imports = [ /home/sander/dydisnix/dydisnix-module.nix ];
services.dydisnixAvahiTest.enable = true;

To allow the coordinator machine to capture the configurations that the target machines publish, we must enable the Avahi system service. In NixOS, this can be done by adding the following lines to /etc/nixos/configuration.nix:


services.avahi.enable = true;

When running the following command-line instruction, the machines' configurations can be captured:


$ dydisnix-geninfra-avahi

Likewise, when setting the following environment variable:


$ export DYDISNIX_GENINFRA=dydisnix-geninfra-avahi

Dynamic Disnix uses the Avahi-discovery service to obtain an infrastructure model.

Writing an augmentation model


The Java version of StaffTracker for example uses MySQL to store data. Typically, it is undesired to publish the authentication credentials over the network (in particular with mDNS, which is quite insecure). We can augment these properties to the captured infrastructure model with the following augmentation model (augment.nix):


{infrastructure, lib}:

lib.mapAttrs (targetName: target:
target // (if target ? containers && target.containers ? mysql-database then {
containers = target.containers // {
mysql-database = target.containers.mysql-database //
{ mysqlUsername = "root";
mysqlPassword = "secret";
};
};
} else {})
) infrastructure

The above model implements a very simple password policy, by iterating over each target machine in the discovered infrastructure model and adding the same mysqlUsername and mysqlPassword property when it encounters a MySQL container service.

Mapping services to machines


In addition to a services model and a dynamically generated (and optionally augmented) infrastructure model, we must map each service to machine in the network using a configured strategy. A strategy can be programmed in a QoS model, such as:


{ services
, infrastructure
, initialDistribution
, previousDistribution
, filters
, lib
}:

let
distribution1 = filters.mapAttrOnList {
inherit services infrastructure;
distribution = initialDistribution;
serviceProperty = "type";
targetPropertyList = "supportedTypes";
};

distribution2 = filters.divideRoundRobin {
distribution = distribution1;
};
in
distribution2

The above QoS model implements the following policy:

  • First, it takes the initialDistribution model that is a cartesian product of all services and machines. It filters the machines on the relationship between the type attribute and the list of supportedTypes. This ensures that services will only be mapped to machines that can host them. For example, a MySQL database should only be deployed to a machine that has a MySQL DBMS installed.
  • Second, it divides the services over the candidate machines using the round robin strategy. That is, it divides services over the candidate target machines in equal proportions and in circular order.

Dynamically deploying a system


With the services model, augmentation model and QoS model, we can dynamically deploy the StaffTracker system (without manually specifying the target machines and their properties, and how to map the services to machines):


$ dydisnix-env -s services.nix -a augment.nix -q qos.nix

The Node.js variant of the StaffTracker example requires unique TCP ports for each web service and web application. By providing the --ports parameter we can include a port assignment specification that is internally managed by dydisnix-port-assign:


$ dydisnix-env -s services.nix -a augment.nix -q qos.nix --ports ports.nix

When providing the --ports parameter, the specification gets automatically updated when ports need to be reassigned.

Making a system self-adaptable from a deployment perspective


With dydisnix-self-adapt we can make a service-oriented system self-adaptable from a deployment perspective -- this tool continuously monitors the network for changes, and runs a redeployment when a change has been detected:


$ dydisnix-self-adapt -s services.nix -a augment.nix -q qos.nix

For example, when shutting down a machine in the network, you will notice that Dynamic Disnix automatically generates a new distribution and redeploys the system to get the missing services back.

Likewise, by adding the ports parameter, you can include port assignments as part of the deployment process:


$ dydisnix-self-adapt -s services.nix -a augment.nix -q qos.nix --ports ports.nix

By adding the --snapshot parameter, we can preemptively capture the state of all stateful services (services annotated with deployState = true; in the services model), such as the databases in which the records are stored. If a machine hosting databases disappears, Disnix can restore the state of the databases elsewhere.


$ dydisnix-self-adapt -s services.nix -a augment.nix -q qos.nix --snapshot

Keep in mind that this feature uses Disnix's snapshotting facilities, which may not be the best solution to manage state, in particular with large databases.

Conclusion


In this blog post, I have described an extended architecture of Dynamic Disnix. In comparison to the previous version, a port assigner has been added that automatically provides unique port numbers to services, and the disnix-snapshot utility that can preemptively capture the state of services, so that they can be restored if a machine disappears from the network.

Despite the fact that Dynamic Disnix has some basic documentation and other improvements from a usability perspective, Dynamic Disnix remains a very experimental prototype that should not be used for any production purposes. In contrast to the basic toolset, I have only used it for testing/demo purposes and I still have no real-life production experience with it. :-)

Moreover, I still have no plans to officially release it yet as many aspects still need to be improved/optimized. For now, you have to obtain the Dynamic Disnix source code from Github and use the included release.nix expression to install it. Furthermore, you probably need to a lot of courage. :-)

Finally, I have extended the Java and Node.js versions of the StaffTracker example as well as the virtual hosts example with simple augmentation and QoS models.

by Sander van der Burg (noreply@blogger.com) at August 22, 2016 09:46 PM

August 10, 2016

Joachim Schiele

tuebix

motivation

managing a 'call for papers' can be a lot of work. the tuebix cfp-software was created in the best practice of KISS.

tuebix

we held a linuxtag at the university of tübingen called tuebix and we had a talk about nixos and a workshop about nixops.

source

concept

the cfp-software backend is written in golang. the frontend was done in materializecss.

the workflow:

  • user fills the form-fields and gets instant feedback because of javascript checks
  • after 'submit' it will generate a json document and send it via email to a mailinglist
  • the mailinglist is monitored manually and people are contacted afterwards manually

after the cfp is over, one can use jq to process the data for creating a schedule.

hosting

security wise it would be good to create a custom user for hosting which was not done here.

/home/joachim/cfp.sh

#!/bin/sh
source /etc/profile
cd /home/joachim/cfp
nix-shell --command "while true; do go run server.go ; done"

systemd job

systemd.services.cfp = {
  wantedBy = [ "multi-user.target" ];
    after = [ "network.target" ];
    serviceConfig = {
      #Type = "forking";
      User = "joachim";
      ExecStart = ''/home/joachim/cfp.sh'';
      ExecStop = ''
      '';
    };
};

reverse proxy

... 
# nixcloud.io (https)
{
  hostName = "nixcloud.io";
  serverAliases = [ "nixcloud.io" "www.nixcloud.io" ];

  documentRoot = "/www/nixcloud.io/";
  enableSSL = true;
  sslServerCert = "/ssl/nixcloud.io-2015.crt";
  sslServerKey = "/ssl/nixcloud.io-2015.key";
  sslServerChain = "/ssl/nixcloud.io-2015-intermediata.der";

  extraConfig = ''
    ...
    RewriteRule ^/cfp$ /cfp/ [R]
    ProxyPass /cfp/ http://127.0.0.1:3000/ retry=0
    ProxyPassReverse /cfp/ http://127.0.0.1:3000/
    ...
  '';
...

summary

using nix-shell it was easy to develop the software and to deploy it to the server. all dependencies are contained.

for further questions drop me an email: js@lastlog.de

by qknight at August 10, 2016 04:35 PM

July 27, 2016

Flying Circus

Vulnix v1.0 release

Intro

Back in May I introduced you to the development of vulnix, a tool which initially was done to find out whether a system (might) be affected by a security vulnerability. It does this by matching the derivations name with the product and version specified in the cpe language of the so-called CVEs (Common Vulnerabilities and Exposures). In the meantime we introduced the tool to the community at the Berlin NixOS Meetup and got some wonderful input in which directions we might extend the features. We sprinted the next two days to improve the code quality and broaden the feature set.

What we got as a result, is best-demonstrated by showing the usage function.

* Is my NixOS system installation affected?

Invoke:  vulnix --system

* Is my user environment (~/.nix-profile) affected?

Invoke:  vulnix --user

* Is my project affected?

Invoke after nix-build:  vulnix ./result

Installation (manual)

With the help of Rok and his recently re-written pypi2nix packaging vulnix for NixOS was a breeze and the installation procedure a simple

git clone https://github.com/flyingcircusio/vulnix.git
cd ./vulnix
nix-build

For a full set of options go for vulnix --help

Platform

From the next release on, vulnix will be part of our platform code and check periodically if the NixOS based VMs are affected or not. In this case operations get informed and can develop counter-measures like introspecting the CVEs, applying patches and or decline the hits as false positives. For instances if the hit is simply coincidental or not relevant in the context of the Flying Circus platform.


by Maksim Bronsky at July 27, 2016 09:21 PM

July 21, 2016

Joachim Schiele

xmlmirror

motivation

we are happy to announce the initial release of a useful new tool called xmlmirror. as the name more or less spells out, xmlmirror is an XML webeditor with schema validation, based on webforms and implemented with codemirror. xmlmirror further uses a library called Fast-XML-Lint which uses libxml2 for schema verification and which is compiled with emscripten. or in layman's terms: a web application that really helps you to create complex XML documents from scratch, as well as fix existing documents that are broken.

live demo / source code

features

more details

selenium

unit testing was implemented using selenium 2.53:

nix-shell -p python35Packages.selenium firefox-bin --command "python3 selenium_test.py"

it works like this:

  1. opens a specially crafted html document: schemainfoCreator-test.html in a webbrowser
  2. executes it and looks for "OK" or a 10 seconds timeout

selenium_test.py:

from selenium import webdriver
from selenium.webdriver.common.keys import Keys

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

ff = webdriver.Firefox()
ff.get("schemainfoCreator-test.html")

assert "schemaInfo unit-test" in ff.title

try:
    element = WebDriverWait(ff, 10).until(EC.presence_of_element_located((By.ID, "OK")))
finally:
    ff.quit()

closure-compiler

the google closure compiler was used to ensure strict typing even though it is javascript:

closure_compiler/jcc schemainfoCreator.js

hint: it was no joy to use this tooling due to a lack of documentation and examples.

fastXmlLint.c

it took a bit of time to get into the internals of libxml2 and the antique API documentation is more confusing than helpful. anyway, two interesting results:

  1. even though xmllint can parse xml documents with a multi-document relax-ng schema it can't be used to parse a multi-document relax-ng schema itself, see discussion

  2. ltrace is your best friend in reverse-engineering shared object/library usage:

    for instance, running:

    ltrace -f xmllint --relaxng html5-rng/xhtml.rng test_fail1.html

    would yield:

    ...
    xmlSAXDefaultVersion(2, 0x40bca9, 116, 112) = 2
    getenv("XMLLINT_INDENT") = nil
    xmlGetExternalEntityLoader(0x7fff26fdca5d, 0x40bd36, 1, 76) = 0x7f969b6c1160
    xmlSetExternalEntityLoader(0x407660, 0x40bd36, 1, 76) = 0x7f969b6c1160
    xmlLineNumbersDefault(1, 0x40bd36, 1, 76) = 0
    xmlSubstituteEntitiesDefault(1, 0x40bd36, 0, 76) = 0
    __xmlLoadExtDtdDefaultValue(1, 0x40bd36, 0, 76) = 0x7f969b9c0a1c
    xmlRelaxNGNewParserCtxt(0x7fff26fdaf23, 0x40bd36, 0, 76) = 0x245c490
    xmlRelaxNGSetParserErrors(0x245c490, 0x404ac0, 0x404ac0, 0x7f969af39080) = 0x245c490
    xmlRelaxNGParse(0x245c490, 0x404ac0, 0x404ac0, 0x7f969af39080) = 0x2527f70
    xmlRelaxNGFreeParserCtxt(0x245c490, 0xffffffff, 0x7f969af38678, 0x25b5570) = 1
    ...

    and this is the exact order of libxml2 function calls xmllint issues to parse test_fail1.html!

    note: this helped us a lot and made it possible to discover the secret xmlLineNumbersDefault function!

emscripten

during this project we had the idea to create a c to javascript cross-compiler abstraction using nix for emscripten and we are happy to announce that it is now officially in nixpkgs, see PR 16208.

this means:

  1. you can use nix-build to cross-compile all your dependencies like libz and afterwards use these in your project
  2. since nix runs on all linuxes, mac os x and other unix-like platforms, you can now enojoy full toolchain automation and deployment when doing emscripten.

if using nixpkgs (master), you can check for emscripten targets using:

nix-env -qaP | grep emscriptenPackages

and install using:

nix-env -iA emscriptenPackages.json_c

note: don't mix json_c (native, x86) with other libs (emscripten, javascript) in your user-profile or you will get weird error messages with object code being in the wrong format and such.

nixos development

nix-shell was the primary development tool along with the default.nix which can basically spawn two different environments:

  • nix-shell -A emEnv - emscripten environment: used to compile c-code in javascript
  • nix-shell -A nativeEnv - native environment: used to develop the c-code in question and also for unit testing purposes

see Makefile.emEnv and Makefile.nativeEnv respectivly.

let's have a look at the default.nix:

let 

...

emEnvironment = stdenv.mkDerivation rec {
  name = "emEnv";
  shellHook = ''
    export HISTFILE=".zsh_history"
    alias make="colormake -f Makefile.emEnv"
    alias c="while true; do inotifywait * -e modify --quiet > /dev/null; clear; make closure| head -n 30; done"
    alias s="python customserver.py"
    alias jcc=closure_compiler/jcc
    echo "welcome to the emEnvironment"
    PS1="emEnv: \$? \w \[$(tput sgr0)\]"
  '';

  buildInputs = [ json-c libz xml-js ] ++ [ colormake nodejs emscripten autoconf automake libtool pkgconfig gnumake strace ltrace python openjdk ncurses ];
};

...

in

{
  # use nix-shell with -A to select the wanted environment to work with:
  #   --pure is optional

  # nix-shell -A nativeEnv --pure  
  nativeEnv = nativeEnvironment;
  # nix-shell -A emEnv --pure  
  emEnv = emEnvironment;
}

you will notice that emEnv is a stdenv.mkDerivation and it uses shellHook and buildInputs.

some remarks:

  • we set a HISTFILE and get a project based history which is nice
  • using alias we override make with colormake and also set the target Makefile to Makefile.emEnv
  • setting a custom PS1 makes it easier to identify the shell when working on n+1 projects at the same time
  • the s alias runs a python webserver with xhtml mime-type support, which is handy when developing with chromium as XHR requests will be working then

note: the default.nix does contain libz, json-c, xml-js packaging and since this is now in nixpkgs it is kind of obsolete now.

conclusion

we (paul/joachim) want to thank Jos van den Oever (prolific open source contributor and co-chair of the OASIS ODF TC on behalf of the Dutch government) for inspiring the creation of this tool. ODF is a prominent example of a real-world standard that leverages the relax ng standard, and we expect xmlmirror to be very useful in the creation of more ODF autotests. Jos has also graciously offered to provide an initial host repository for xmlmirror.

  • schema parsing in codemirror can now easily be extended with all relax ng schemas!

  • also thanks to profpatsch for his explanations on the nix feature called override, see emscripten-packages.nix

  • we also want to thank nlnet foundation for their financial contribution from the ODF fund which enabled us to complete this interesting project. thanks as well to Michiel Leenaars (not only from nlnet but also one of the people behind the ODF plugfests) for his interest in the project. now we have a real powerful xmleditor, made huge progress with the emscripten toolchain on nixos and have created a pretty useful development workflow.

if you have questions/comments, see nixcloud.io for contact details.

by qknight at July 21, 2016 08:35 AM

July 06, 2016

Rok Garbas

pypi2nix reborn

In the recent years pypi2nix tool went over many iterations. Quite few approaches were tested, and for the last year all pieces finally came together. I can say personally that for the past 6 months I'm a happy pypi2nix user. I finally came around to polish some rough edges and write this blogpost.

Currently I'm looking for python developers in Nix community to give it a try and report bugs, file feature requests or ask questions if something is unclear.

What is pypi2nix?

pypi2nix is tool that tries to generate nix expressions from your projects requirements.txt, buildout.cfg and setup.py.

Python packaging is not the simplest thing you can read all about in one place. It is years of poorly documented work and somehow it all works (well at least most of the time). pypi2nix is never going to be a tool that will work 100% of the time, but in the worse case it will get you pretty close and leave you with only few lines of manual work.

An important thing to keep in mind is that pypi2nix is not a tool that will automate generating pkgs/top-level/python-packages.nix for nixpkgs repository. pypi2nix should be used on project basis (similar to how cabal2nix works). Maybe this will change in the future, but for now this is the current scope of the project.

How do you install it?

pypi2nix was just pushed to nixpkgs master and should take some time until it lands in your channels. You can install it directly from master branch on Github.

% git clone https://github.com/garbas/pypi2nix
% cd pypi2nix
% nix-env -iA build."x86_64-linux" -f release.nix

Once pypi2nix gets built by hydra you can also install it via nix-env command:

% nix-env -iA nixos.pypi2nix

or if you are using Nix on non-NixOS system

% nix-env -iA nixpkgs.pypi2nix

If you want to start contributing to pypi2nix look no further then running nix-shell:

% nix-shell

and you can start hacking on the code.

How do you use it?

It is very common for python project to have requirements.txt file which lists projects dependencies.

For the sake of this blogpost lets create an example requirements.txt

% echo "requests"   >  requirements.txt
% echo "pyramid"    >> requirements.txt
% echo "lxml"       >> requirements.txt

As you can see above I created a requirements.txt file where I specified 3 dependencies. To generate nix expressions we have to run:

% pypi2nix -r requirements.txt -E "libxslt libxml2"
...

Because lxml depends on libxslt and libxml2 we needed to declare them as build input (-E options) in order to install it.

For those new to python packaging world keep in minf that we need to install a package in order to know which are its dependencies. Crazy right!? Well that is how python works.

Above command created 3 files:

  • requirements_generated.nix - A list of generated nix expressions for all packages listed in requirements.txt and all their dependencies. An example of generated expression

    { pkgs, python, commonBuildInputs ? [], commonDoCheck ? false }:
    
    self: {
    
      ...
    
      "Babel" = python.mkDerivation {
        name = "Babel-2.3.4";
        src = pkgs.fetchurl {
          url = "https://files.pythonhosted.org/packages/.../Babel-2.3.4.tar.gz";
          sha256= "...";
        };
        doCheck = commonDoCheck;
        buildInputs = commonBuildInputs;
        propagatedBuildInputs = [
          self."pytz"
        ];
        meta = {
          homepage = "";
          license = lib.bsdOriginal;
          description = "Internationalization utilities";
        };
      };
    
      ...
    
    }
    
  • requirements_override.nix - It is an empty list of overrides that you can write if you are not happy with what pypi2nix generated. This file only gets created if it does not exist yet. More on this later on.

  • requirements.nix - A file that glues together requirements_generated.nix and requirements_override.nix. Also in this file a new python nix functions are implemented. More on this later on.

To build pyramid, lxml and requests do:

% nix-build requirements.nix -A pkgs.pyramid -A pkgs.lxml -A pkgs.requests

Or to build python interpreter with all of the above packages

% nix-build requirements.nix -A interpreter
% ./result/bin/python -c "import pyramid; import lxml; import requests"

Or to enter development environment with all of the above packages

% nix-shell requirements.nix -A interpreter
(nix-shell) % python -c "import pyramid; import lxml; import requests"

By default python 2.7 is selected, but you can choose other python version by specifying -V option

% pypi2nix -r requirements.txt -E "libxslt libxml2" -V "3.5"
...
% nix-shell requirements.nix -A interpreter
(nix-shell) % python3 -c "import pyramid; import lxml; import requests"

All python version in nixpkgs are available in pypi2nix as well

% pypi2nix --help
...
Options:
   ...
   -V, --python-version [2.7|3.5|3.4|3.3|pypy|2.6|3.2]
                                  Provide which python version we build for.
                                  [required]

You can find few more examples in examples folder.

What to do when nix-build fails?

It might (and probably will) happen that some packages will fail to build. There are milion of reasons why some packages will fail to build. It would be foolish trying to solve and accommodate every possible scenario. Instead, when things go south, tools to override generated expressions are provided.

Initially a file with suffix _override.nix is generated with an empty list of overrides.

As an example to show how this overriding works, let us enable tests for lxml library from previous example. A requirements_override.nix file would then look like

{ pkgs, python }:

self: super: {

  "lxml" = python.overrideDerivation super."lxml" (old: {
    doCheck = true;
  });

}

After the change you can continue to build packages as shown above. If you rerun pypi2nix -r requirements.txt you will see that requirements_override.nix does not get overwritten.

Above example gives you all the flexibility to override any existing expression as well as adding new (manually written) expressions.

Why are generated expressions different then the one used in nixpkgs?

If you look at generated nix expressions in requirements_generated.nix and if you packaged python packages with nix in the past you will see that the functions used there are a bit different.

With pypi2nix I also took the freedome to explore how could we have nicer developer experience when working with python and nix. Nothing is set in stone, so I invite you to open an issue if you disagree with the the direction taken.

Current limitations

Current limitations of pypi2nix tool that are going to be fixed in the future are:

  • not possible to specify multiple requirements.txt files (#34)
  • not working with buildout.cfg files (#31)
  • not working with on setup.py files (#32)
  • tests for generated packages are disabled by default for now (#35)
  • requires packages on pypi to have a tarball/zip release. some packages only publish in egg/wheel format (#26)

Let me know if I'm missing some crutial things that I just can not see.

What does the future hold?

pypi2nix is just a first stepping stone on the roadmap to package python packages on PyPI.

End goal for me is that an average python developer (or a consumer of python packages) would be able to depend on any python package without writing a single line of nix code.

Next thing towards that goal I am going to work on is to create a repository of a smaller set automatically generated nix expressions from PyPi. Not sure where the experiment will take me, but I have used similar approach in few previous projects.

I already started a repository called nixpkgs-python where I plan to continue this work, in case you are interested in following/joining this effort.

Convinced to give it a try?

Help me test pypi2nix and provide me with failing requirements.txt. And if it works please give me a shout over twitter. It is nice to know that it is working for somebody.

by Rok Garbas at July 06, 2016 10:00 PM

July 02, 2016

Rok Garbas

NixOS Meetup Report

All together we were 17 people, which was the biggest number so far for any of NixOS Berlin gatherings. I'm not sure what was the reason, but I my speculations are that an evening of gathering is nice from time to time (presentations, demos, ...), but a lot more Nixers want to hack together and an evening is just to short. A day or two of hacking feels like you can actually get something done. We will definetly repeat it.

Also at this point I would like to thank Mozilla for letting us use their office and to keep us hydrated for all those hard working days.

Beginner workshop

We started the meetup with a Nix/NixOS walk through for anybody that is just starting with Nix/NixOS. We got 5 newcomers to Nix/NixOS.

We also had some interesting talks afterwards how we can improve this initial Nix on-boarding. There is just soo much we can improve. I know I personally learned a lot how to improve explaining Nix to a newcomer and I can't wait until next time I have a chance to do it.

For those who of you who did not have time to join us for this Nix/NixOS introduction workshop, you can rest assure we will repeat it very soon.

Presentations / Demos

Each day we reserved ~1 hour to relax from coding and listen to people that want to present/demonstrate something they built with Nix or some interesting tooling around Nix. We did not have any camera around to record it, and also presentations were more discussions then really presentations.

Reports

Many people worked on many different projects, here are individual short reports. I hope all of this give you inspiration for you work and hopefully you will join us next time.


Emery,

To summarize, I added color output to nix-repl, and worked on a new Nixpkgs stdenv for Genode. https://github.com/ehmry


Christian Kauhaus,

Known as @ckauhaus on IRC & Twitter.

Blogging on https://blog.flyingcircus.io from time to time.

Topics:

  • Got first hands-on experience with nixops, installing lots of random stuff into an ever-changing set of virtual machines on my laptop. Struggled with a corrupted nix store on my host system. Unfortunatly, nix does not give clear error messages in this case so I lost a lot of time here.
  • Hacked heavily on vulnix. An initial release should be ready soon.

lassulus

  • I learned how FHSUserEnv are working. Used them to install some closed source apps on my workstation. - I updated my build buildbot configuration to use the correct version of nixpkgs for my tests
  • I extended some services in my nixos configuration.

Code can be found in my nixos-configuration repository:

http://cgit.lassul.us/stockholm/


Maksim Bronsky

@dvhfm

  • I introduced vulnix to the community. Had some great reactions and discussions of where the development should be headed
  • I coded on one of the caching mechanisms of vulnix (xml parsing) to improve the overall runtime.

Matthias Heinzel Matthias Heinzel

Great meetup, I really enjoyed it. Looking forward to the next one. :)

I mainly set up my NixOS system, got my wifi working (yay!) and learnt a bit about the Nix language. Thanks for all your help!

I also plan to help sjordan with the graphical user interface for Nix.

mheinzel on github and IRC.


sjordan

Enjoyed the meetup. Helped people to get started on nixos/nix.

I had great discussions about different aspects of nix, especially nix as a package manager which really helped me develop concepts for a GUI for nix.

nixgui: http://hub.darcs.net/seppeljordan/nixgui


Maarten Hoogendoorn

twtiter: @moretea_nl github: moretea

  • Helped to install NixOS on a macbook
  • Worked with Fabian on experimenting with adding documentation comments to nix, to allow attr set arguments to be documented in his list-package-options branch. Some initial code: https://github.com/moretea/nix/commit/9be41e4110983604367ee796a03aab4114a7bdbf (see tests/lang/eval-okay-functionargs-docs.exp and tests/lang/eval-okay-functionargs-docs.nix in the repo for what this actually does).
  • Hacked a way to document library functions in a structured way, this could be extended (see last commit) to support runtime type checking of functions in nix itself. See https://www.youtube.com/watch?v=ahVu3tjrriM

Mathias Schreck

github: @lo1tuma twiltter: @lo1tuma

The meetup was a really great event, as a nix newbie a learned a lot.

  • Learned about the difference between import and callPackage
  • I learned a lot about nixops and finally managed to deploy our custom jenkins package (which is based on this expression https://github.com/zalora/microgram/blob/59dfe04d2ac67945f6d2dee5f7233b0cdba9318d/pkgs/jenkins/default.nix) to virtualbox and EC2. Now we have all our jenkins config 100% as code which is much better than configuring stuff via the jenkins web UI.
  • I learned about buildFHSUserEnv. One of our jenkins plugins uses a hardcoded path to /bin/echo which I could make available through buildFHSUserEnv. I also created an upstream issue and I try provide a patch for jenkins soon.

Rok Garbas

github/twitter/irc: garbas

  • closed few wiki tickets. discovered how much I suck at writting documentation. too many interuptions to get something concretly done. but a small progress was done. anyone that want to help port remaining wiki tickets to respected manuals please select your pick:

    https://github.com/NixOS/nixpkgs/milestones/Move%20the%20wiki!

  • got familiar with vulnix. can't wait to use it for my own work. packaged the project with help of pypi2nix. helped port code from argparse to click

  • fix a lot of bugs for pypi2nix. currently i'm looking for python developers to help me test if it is working for them. separate blog post on this topic is will be written.

Final thoughts

It was fun and we also got work done. Hooray for us! I can not wait for next time we meet again. Let me know if you have any ideas for next nixos meetup (ping me on Twitter).

by Rok Garbas at July 02, 2016 10:00 PM

NixOS Meetup Report

All together we were 17 people, which was the biggest number so far for any of NixOS Berlin gatherings. I'm not sure what was the reason, but my speculations are that an evening of gathering is nice from time to time (presentations, demos, ...), but a lot more Nixers want to hack together and an evening is just to short. A day or two of hacking feels like you can actually get something done. We will definitely repeat it.

Also at this point I would like to thank Mozilla for letting us use their office and to keep us hydrated for all those hard working days.

Beginner workshop

We started the meetup with a Nix/NixOS walk through for anybody that is just starting with Nix/NixOS. We got 5 newcomers to Nix/NixOS.

We also had some interesting talks afterwards how we can improve this initial Nix on-boarding. There is just soo much we can improve. I know I personally learned a lot how to improve explaining Nix to a newcomer and I can't wait until next time I have a chance to do it.

For those who of you who did not have time to join us for this Nix/NixOS introduction workshop, you can rest assured we will repeat it very soon.

Presentations / Demos

Each day we reserved ~1 hour to relax from coding and listen to people that want to present/demonstrate something they built with Nix or some interesting tooling around Nix. We did not have any camera around to record it, and also presentations were more discussions then really presentations.

Reports

Many people worked on many different projects, here are individual short reports. I hope all of this give you inspiration for you work and hopefully you will join us next time.


Emery,

To summarize, I added color output to nix-repl, and worked on a new Nixpkgs stdenv for Genode. https://github.com/ehmry


Christian Kauhaus,

Known as @ckauhaus on IRC & Twitter.

Blogging on https://blog.flyingcircus.io from time to time.

Topics:

  • Got first hands-on experience with nixops, installing lots of random stuff into an ever-changing set of virtual machines on my laptop. Struggled with a corrupted nix store on my host system. Unfortunatly, nix does not give clear error messages in this case so I lost a lot of time here.
  • Hacked heavily on vulnix. An initial release should be ready soon.

lassulus

  • I learned how FHSUserEnv are working. Used them to install some closed source apps on my workstation. - I updated my build buildbot configuration to use the correct version of nixpkgs for my tests
  • I extended some services in my nixos configuration.

Code can be found in my nixos-configuration repository:

http://cgit.lassul.us/stockholm/


Maksim Bronsky

@dvhfm

  • I introduced vulnix to the community. Had some great reactions and discussions of where the development should be headed
  • I coded on one of the caching mechanisms of vulnix (xml parsing) to improve the overall runtime.

Matthias Heinzel Matthias Heinzel

Great meetup, I really enjoyed it. Looking forward to the next one. :)

I mainly set up my NixOS system, got my wifi working (yay!) and learnt a bit about the Nix language. Thanks for all your help!

I also plan to help sjordan with the graphical user interface for Nix.

mheinzel on github and IRC.


sjordan

Enjoyed the meetup. Helped people to get started on nixos/nix.

I had great discussions about different aspects of nix, especially nix as a package manager which really helped me develop concepts for a GUI for nix.

nixgui: http://hub.darcs.net/seppeljordan/nixgui


Maarten Hoogendoorn

twtiter: @moretea_nl github: moretea

  • Helped to install NixOS on a macbook
  • Worked with Fabian on experimenting with adding documentation comments to nix, to allow attr set arguments to be documented in his list-package-options branch. Some initial code: https://github.com/moretea/nix/commit/9be41e4110983604367ee796a03aab4114a7bdbf (see tests/lang/eval-okay-functionargs-docs.exp and tests/lang/eval-okay-functionargs-docs.nix in the repo for what this actually does).
  • Hacked a way to document library functions in a structured way, this could be extended (see last commit) to support runtime type checking of functions in nix itself. See https://www.youtube.com/watch?v=ahVu3tjrriM

Mathias Schreck

github: @lo1tuma twiltter: @lo1tuma

The meetup was a really great event, as a nix newbie a learned a lot.

  • Learned about the difference between import and callPackage
  • I learned a lot about nixops and finally managed to deploy our custom jenkins package (which is based on this expression https://github.com/zalora/microgram/blob/59dfe04d2ac67945f6d2dee5f7233b0cdba9318d/pkgs/jenkins/default.nix) to virtualbox and EC2. Now we have all our jenkins config 100% as code which is much better than configuring stuff via the jenkins web UI.
  • I learned about buildFHSUserEnv. One of our jenkins plugins uses a hardcoded path to /bin/echo which I could make available through buildFHSUserEnv. I also created an upstream issue and I try provide a patch for jenkins soon.

Rok Garbas

github/twitter/irc: garbas

  • closed few wiki tickets. discovered how much I suck at writting documentation. too many interuptions to get something concretly done. but a small progress was done. anyone that want to help port remaining wiki tickets to respected manuals please select your pick:

    https://github.com/NixOS/nixpkgs/milestones/Move%20the%20wiki!

  • got familiar with vulnix. can't wait to use it for my own work. packaged the project with help of pypi2nix. helped port code from argparse to click

  • fix a lot of bugs for pypi2nix. currently i'm looking for python developers to help me test if it is working for them. separate blog post on this topic is will be written.

Final thoughts

It was fun and we also got work done. Hooray for us! I can not wait for next time we meet again. Let me know if you have any ideas for next nixos meetup (ping me on Twitter).

by Rok Garbas at July 02, 2016 10:00 PM

NixOS Meetup Report

All together we were 17 people, which was the biggest number so far for any of NixOS Berlin gatherings. I'm not sure what was the reason, but I my speculations are that an evening of gathering is nice from time to time (presentations, demos, ...), but a lot more Nixers want to hack together and an evening is just to short. A day or two of hacking feels like you can actually get something done. We will definetly repeat it.

Also at this point I would like to thank Mozilla for letting us use their office and to keep us hydrated for all those hard working days.

Beginner workshop

We started the meetup with a Nix/NixOS walk through for anybody that is just starting with Nix/NixOS. We got 5 newcomers to Nix/NixOS.

We also had some interesting talks afterwards how we can improve this initial Nix on-boarding. There is just soo much we can improve. I know I personally learned a lot how to improve explaining Nix to a newcomer and I can't wait until next time I have a chance to do it.

For those who of you who did not have time to join us for this Nix/NixOS introduction workshop, you can rest assure we will repeat it very soon.

Presentations / Demos

Each day we reserved ~1 hour to relax from coding and listen to people that want to present/demonstrate something they built with Nix or some interesting tooling around Nix. We did not have any camera around to record it, and also presentations were more discussions then really presentations.

Reports

Many people worked on many different projects, here are individual short reports. I hope all of this give you inspiration for you work and hopefully you will join us next time.


Emery,

To summarize, I added color output to nix-repl, and worked on a new Nixpkgs stdenv for Genode. https://github.com/ehmry


Christian Kauhaus,

Known as @ckauhaus on IRC & Twitter.

Blogging on https://blog.flyingcircus.io from time to time.

Topics:

  • Got first hands-on experience with nixops, installing lots of random stuff into an ever-changing set of virtual machines on my laptop. Struggled with a corrupted nix store on my host system. Unfortunatly, nix does not give clear error messages in this case so I lost a lot of time here.
  • Hacked heavily on vulnix. An initial release should be ready soon.

lassulus

  • I learned how FHSUserEnv are working. Used them to install some closed source apps on my workstation. - I updated my build buildbot configuration to use the correct version of nixpkgs for my tests
  • I extended some services in my nixos configuration.

Code can be found in my nixos-configuration repository:

http://cgit.lassul.us/stockholm/


Maksim Bronsky

@dvhfm

  • I introduced vulnix to the community. Had some great reactions and discussions of where the development should be headed
  • I coded on one of the caching mechanisms of vulnix (xml parsing) to improve the overall runtime.

Matthias Heinzel Matthias Heinzel

Great meetup, I really enjoyed it. Looking forward to the next one. :)

I mainly set up my NixOS system, got my wifi working (yay!) and learnt a bit about the Nix language. Thanks for all your help!

I also plan to help sjordan with the graphical user interface for Nix.

mheinzel on github and IRC.


sjordan

Enjoyed the meetup. Helped people to get started on nixos/nix.

I had great discussions about different aspects of nix, especially nix as a package manager which really helped me develop concepts for a GUI for nix.

nixgui: http://hub.darcs.net/seppeljordan/nixgui


Maarten Hoogendoorn

twtiter: @moretea_nl github: moretea

  • Helped to install NixOS on a macbook
  • Worked with Fabian on experimenting with adding documentation comments to nix, to allow attr set arguments to be documented in his list-package-options branch. Some initial code: https://github.com/moretea/nix/commit/9be41e4110983604367ee796a03aab4114a7bdbf (see tests/lang/eval-okay-functionargs-docs.exp and tests/lang/eval-okay-functionargs-docs.nix in the repo for what this actually does).
  • Hacked a way to document library functions in a structured way, this could be extended (see last commit) to support runtime type checking of functions in nix itself. See https://www.youtube.com/watch?v=ahVu3tjrriM

Mathias Schreck

github: @lo1tuma twiltter: @lo1tuma

The meetup was a really great event, as a nix newbie a learned a lot.

  • Learned about the difference between import and callPackage
  • I learned a lot about nixops and finally managed to deploy our custom jenkins package (which is based on this expression https://github.com/zalora/microgram/blob/59dfe04d2ac67945f6d2dee5f7233b0cdba9318d/pkgs/jenkins/default.nix) to virtualbox and EC2. Now we have all our jenkins config 100% as code which is much better than configuring stuff via the jenkins web UI.
  • I learned about buildFHSUserEnv. One of our jenkins plugins uses a hardcoded path to /bin/echo which I could make available through buildFHSUserEnv. I also created an upstream issue and I try provide a patch for jenkins soon.

Rok Garbas

github/twitter/irc: garbas

  • closed few wiki tickets. discovered how much I suck at writting documentation. too many interuptions to get something concretly done. but a small progress was done. anyone that want to help port remaining wiki tickets to respected manuals please select your pick:

    https://github.com/NixOS/nixpkgs/milestones/Move%20the%20wiki!

  • got familiar with vulnix. can't wait to use it for my own work. packaged the project with help of pypi2nix. helped port code from argparse to click

  • fix a lot of bugs for pypi2nix. currently i'm looking for python developers to help me test if it is working for them. separate blog post on this topic is will be written.

Final thoughts

It was fun and we also got work done. Hooray for us! I can not wait for next time we meet again. Let me know if you have any ideas for next nixos meetup (ping me on Twitter).

by Rok Garbas at July 02, 2016 10:00 PM

June 28, 2016

Joachim Schiele

nixos-augsburg-sprint

nixos-sprint

paul and me visisted the augsburger openlab last weekend, for a nice nixos sprint with profpatsch. the sprint lasted two days and we talked lots about nix/nixpkgs internals.

nix based emscripten toolchain

we've been working on the emscripten nix toolchain. current status is: prototype is working and we can already compile these targets:

note: YAY, this is the first nix-based emscripten toolchain which should work on nixos but also on mac os x and on basically every POSIX supporting unix!

nixexpr grammar

we had the idea to make the nix expression language more forgiving by having it support ; at the end of a normal function body.

random example: mkDerivation

  json_c_ = emscriptenStdenv.mkDerivation rec {

    name = "json_c_";
    version = "json-c-0.12-20140410";

    buildInputs = [ autoconf automake libtool pkgconfig gnumake ];

    src = fetchgit {
      url = "https://github.com/json-c/json-c";
      rev = "refs/tags/${version}";
      sha256 = "0s9h6147v2vkd4l4k3prg850n0k1mcbhwhbr09dzq97m6vi9lfdi";
    };
    postFixup = "echo postFixup";
    preFixup = "echo preFixup";
    fixupPhase = "echo fixupPhase";
  };

you close the scope with }; and you always need the ; which is not optional.

function call

{ foo, bar } :

{
  # function body
}

you close the scope with } and you are not allowed to use };!

note: with this patch you are now allowed to write either } (default) or }; which is new.

mkDerivation outputs

i finally learned that we now have the outputs-feature

this means nixos/nix now supports split packages:

  • foo.deb vs.
  • foo-dev.deb

as ubuntu and other distros does.

in nixpkgs, for instance in an stdenv.mkDerivation you can now use:

outputs = [ "lib" "headers" "doc" ];

to install software into these directories! great!

summary

nice ppl, nice room. 10/10, would hack there again! huge thanks to Profpatsch and helpers.

by qknight at June 28, 2016 02:54 AM

June 21, 2016

Joachim Schiele

android calendar and CalDav

motivation

2016 and android still does not have a built-in open source CalDAV/CardDAV, this article is a very interesting read on the history and reactions of people wanting that. there is a nice article on how to implement a CalDAV client.

so in a nutshell: syncing different devices while supporting offline capabilities is complicated.

long story short: i do not want to copy my data into the google cloud. in this this posting i want to share my results with using owncloud 8.2 to sync my calendar and address book between my desktop computer and android (cyanogenmod) mobile.

owncloud/PIM

used components:

results

what i liked:

  • have my own infrastructure
  • many components are open source

what i hate:

  • lots of manual deployemnt and configuration
  • android
    • battery drain on android: 4 programs periodically pull for updates
    • if owncloud can't be reached aCalDAV will always set a notification with an error, see issue 17

  • laptop: thunderbird's CalDAV implementation
    • focus stealing warning popup-dialog is displayed once in a while -> very annoying, see issue 1287332

      to reproduce this:

      1. shutdown your owncloud server
      2. start thunderbird
      3. add a new event in the calendar and see thunderbird-> tools -> error console

        Timestamp: 21.06.2016 15:01:54
        Error: An error occurred when writing to the calendar http://192.168.0.86/owncloud/remote.php/dav/calendars/joachim/default/! Error code: MODIFICATION_FAILED. Description: 
        Source File: resource://calendar/modules/calUtils.jsm -> file:///home/joachim/.thunderbird/hy2x4cxy.default/extensions/%7Be2fda1a4-762b-4020-b5ad-a41df1933103%7D/calendar-js/calCalendarManager.js
        Line: 959
      4. see the dialog

      5. restart thunderbird, see it once in a while
      6. once owncloud is connected and synced, the 'added and cached' event is synced and the message will not appear anymore

  • laptop: thunderbird's CardDAV implementation interacts stangely with the SoGo connector, that is:
    • don't try mass-moving of your contacts from the offline-address books into your owncloud address book. with old versions you might hit this bug
    • address book can only be editited when SoGo connector can communicate with owncloud, no offline functionality
    • sometimes one deletes an entry in the address book, it vanishes, appears again and finally vanishes forver ...?

conclusion

i love this setup even as is a bit fragile! k9 mail is a great client, has even a better thread view than thunderbird. etar is exactly the calendar app i wanted! owncloud 8.x/9.x is currently packaged in nixpkgs but is broken. next up: fix the owncloud package(s) on nixos/nixcloud and use that instead of ubuntu 16.03.

by qknight at June 21, 2016 11:54 AM

Sheena Artrip

NixOS Recipes - ELK Stack

Walkthrough of a simple ELK stack configuration in NixOS, followed by a more advanced introduction to functions and services

June 21, 2016 04:58 AM

June 20, 2016

Sander van der Burg

Using Disnix as a remote package deployer

Recently, I was asked whether it is possible to use Disnix as a tool for remote package deployment.

As described in a number of earlier blog posts, Disnix's primary purpose is not remote (or distributed) package management, but deploying systems that can be decomposed into services to networks of machines. To deploy these kinds of systems, Disnix executes all required deployment activities, including building services from source code, distributing them to target machines in the network and activating or deactivating them.

However, a service deployment process is basically a superset of an "ordinary" package deployment process. In this blog post, I will describe how we can do remote package deployment by instructing Disnix to only use a relevant subset of features.

Specifying packages as services


In the Nix packages collection, it is a common habit to write each package specification as a function in which the parameters denote the (local) build and runtime dependencies (something that Disnix's manual refers to as intra-dependencies) that the package needs. The remainder of the function describes how to build the package from source code and its provided dependencies.

Disnix has adopted this habit and extended this convention to services. The main difference between Nix package expressions and Disnix service expressions is that the latter also take inter-dependencies into account that refer to run-time dependencies on services that may have been deployed to other machines in the network. For services that have no inter-dependencies, a Disnix expression is identical to an ordinary package expression.

This means that, for example, an expression for a package such as the Midnight Commander is also a valid Disnix service with no inter-dependencies:


{ stdenv, fetchurl, pkgconfig, glib, gpm, file, e2fsprogs
, libX11, libICE, perl, zip, unzip, gettext, slang
}:

stdenv.mkDerivation {
name = "mc-4.8.12";

src = fetchurl {
url = http://www.midnight-commander.org/downloads/mc-4.8.12.tar.bz2;
sha256 = "15lkwcis0labshq9k8c2fqdwv8az2c87qpdqwp5p31s8gb1gqm0h";
};

buildInputs = [ pkgconfig perl glib gpm slang zip unzip file gettext
libX11 libICE e2fsprogs ];

meta = {
description = "File Manager and User Shell for the GNU Project";
homepage = http://www.midnight-commander.org;
license = "GPLv2+";
maintainers = [ stdenv.lib.maintainers.sander ];
};
}

Composing packages locally


Package and service expressions are functions that do not specify the versions or variants of the dependencies that should be used. To allow services to be deployed, we must compose them by providing the desired versions or variants of the dependencies as function parameters.

As with ordinary Nix packages, Disnix has also adopted this convention for services. In addition, we have to compose a Disnix service twice -- first its intra-dependencies and later its inter-dependencies.

Intra-dependency composition in Disnix is done in a similar way as in the Nix packages collection:


{pkgs, system}:

let
callPackage = pkgs.lib.callPackageWith (pkgs // self);

self = {
pkgconfig = callPackage ./pkgs/pkgconfig { };

gpm = callPackage ./pkgs/gpm { };

mc = callPackage ./pkgs/mc { };
};
in
self

The above expression (custom-packages.nix) composes the Midnight Commander package by providing its intra-dependencies as function parameters. The third attribute (mc) invokes a function named: callPackage {} that imports the previous package expression and automatically provides the parameters having the same names as the function parameters.

The callPackage { } function first consults the self attribute set (that composes some of Midnight Commander's dependencies as well, such as gpm and pkgconfig) and then any package from the Nixpkgs repository.

Writing a minimal services model


Previously, we have shown how to build packages from source code and its dependencies, and how to compose packages locally. For the deployment of services, more information is needed. For example, we need to compose their inter-dependencies so that services know how to reach them.

Furthermore, Disnix's end objective is to get a running service-oriented system and carries out extra deployment activities for services to accomplish this, such as activation and deactivation. The latter two steps are executed by a Dysnomia plugin that is determined by annotating a service with a type attribute.

For package deployment, specifying these extra attributes and executing these remaining activities are in principle not required. Nonetheless, we still need to provide a minimal services model so that Disnix knows which units can be deployed.

Exposing the Midnight Commander package as a service, can be done as follows:


{pkgs, system, distribution, invDistribution}:

let
customPkgs = import ./custom-packages.nix {
inherit pkgs system;
};
in
{
mc = {
name = "mc";
pkg = customPkgs.mc;
type = "package";
};
}

In the above expression, we import our intra-dependency composition expression (custom-packages.nix), and we use the pkg sub attribute to refer to the intra-dependency composition of the Midnight Commander. We annotate the Midnight Commander service with a package type to instruct Disnix that no additional deployment steps need to be performed beyond the installation of the package, such activation or deactivation.

Since the above pattern is common to all packages, we can also automatically generate services for any package in the composition expression:


{pkgs, system, distribution, invDistribution}:

let
customPkgs = import ./custom-packages.nix {
inherit pkgs system;
};
in
pkgs.lib.mapAttrs (name: pkg: {
inherit name pkg;
type = "package";
}) customPkgs

The above services model exposes all packages in our composition expression as a service.

Configuring the remote machine's search paths


With the services models shown in the previous section, we have all ingredients available to deploy packages with Disnix. To allow users on the remote machines to conveniently access their packages, we must add Disnix' Nix profile to the PATH of a user on the remote machines:


$ export PATH=/nix/var/nix/profiles/disnix/default/bin:$PATH

When using NixOS, this variable can be extended by adding the following line to /etc/nixos/configuration.nix:


environment.variables.PATH = [ "/nix/var/nix/profiles/disnix/default/bin" ];

Deploying packages with Disnix


In addition to a services model, Disnix needs an infrastructure and distribution model to deploy packages. For example, we can define an infrastructure model that may look as follows:


{
test1.properties.hostname = "test1";
test2 = {
properties.hostname = "test2";
system = "x86_64-darwin";
};
}

The above infrastructure model describes two machines that have hostname test1 and test2. Furthermore, machine test2 has a specific system architecture: x86_64-darwin that corresponds to a 64-bit Intel-based Mac OS X.

We can distribute packages to these two machines with the following distribution model:


{infrastructure}:

{
gpm = [ infrastructure.test1 ];
pkgconfig = [ infrastructure.test2 ];
mc = [ infrastructure.test1 infrastructure.test2 ];
}

In the above distribution model, we distribute package gpm to machine test1, pkgconfig to machine test2 and mc to both machines.

When running the following command-line instruction:


$ disnix-env -s services.nix -i infrastructure.nix -d distribution.nix

Disnix executes all activities to get the packages in the distribution model deployed to the machines, such as building them from source code (including its dependencies), and distributing their dependency closures to the target machines.

Because machine test2 may have a different system architecture as the coordinator machine responsible for carrying out the deployment, Disnix can use Nix's delegation mechanism to forward a build to a machine that is capable of doing it.

Alternatively, packages can also be built on the target machines through Disnix:


$ disnix-env --build-on-targets \
-s services.nix -i infrastructure.nix -d distribution.nix

After the deployment above command-line instructions have succeeded, we should be able to start the Midnight Commander on any of the target machines, by running:


$ mc

Deploying any package from the Nixpkgs repository


Besides deploying a custom set of packages, it is also possible to use Disnix to remotely deploy any package in the Nixpkgs repository, but doing so is a bit tricky.

The main challenge lies in the fact that the Nix packages set is a nested set of attributes, whereas Disnix expects services to be addressed in one attribute set only. Fortunately, the Nix expression language and Disnix models are flexible enough to implement a solution. For example, we can define a distribution model as follows:


{infrastructure}:

{
mc = [ infrastructure.test1 ];
git = [ infrastructure.test1 ];
wget = [ infrastructure.test1 ];
"xlibs.libX11" = [ infrastructure.test1 ];
}

Note that we use a dot notation: xlibs.libX11 as an attribute name to refer to libX11 that can only be referenced as a sub attribute in Nixpkgs.

We can write a services model that uses the attribute names in the distribution model to refer to the corresponding package in Nixpkgs:


{pkgs, system, distribution, invDistribution}:

pkgs.lib.mapAttrs (name: targets:
let
attrPath = pkgs.lib.splitString "." name;
in
{ inherit name;
pkg = pkgs.lib.attrByPath attrPath
(throw "package: ${name} cannot be referenced in the package set")
pkgs;
type = "package";
}
) distribution

With the above service model we can deploy any Nix package to any remote machine with Disnix.

Multi-user package management


Besides supporting single user installations, Nix also supports multi-user installations in which every user has its own private Nix profile with its own set of packages. With Disnix we can also manage multiple profiles. For example, by adding the --profile parameter, we can deploy another Nix profile that, for example, contains a set of packages for the user: sander:


$ disnix-env -s services.nix -i infrastructure.nix -d distribution.nix \
--profile sander

The user: sander can access its own set of packages by setting the PATH environment variable to:


$ export PATH=/nix/var/nix/profiles/disnix/sander:$PATH

Conclusion


Although Disnix has not been strictly designed for this purpose, I have described in this blog post how Disnix can be used as a remote package deployer by using a relevant subset of Disnix features.

Moreover, I now consider the underlying Disnix primitives to be mature enough. As such, I am announcing the release of Disnix 0.6!

Acknowledgements


I gained the inspiration for writing this blog post from discussions with Matthias Beyer on the #nixos IRC channel.

by Sander van der Burg (noreply@blogger.com) at June 20, 2016 08:38 PM