···1getVersion() {
2 local dir="$1"
3 rev=
4- if [ -e "$dir/.git" ]; then
05 if [ -z "$(type -P git)" ]; then
6 echo "warning: Git not found; cannot figure out revision of $dir" >&2
7 return
8 fi
9 cd "$dir"
10- rev=$(git rev-parse --short HEAD)
11- if git describe --always --dirty | grep -q dirty; then
12 rev+=M
13 fi
14 fi
···1getVersion() {
2 local dir="$1"
3 rev=
4+ gitDir="$dir/.git"
5+ if [ -e "$gitDir" ]; then
6 if [ -z "$(type -P git)" ]; then
7 echo "warning: Git not found; cannot figure out revision of $dir" >&2
8 return
9 fi
10 cd "$dir"
11+ rev=$(git --git-dir="$gitDir" rev-parse --short HEAD)
12+ if git --git-dir="$gitDir" describe --always --dirty | grep -q dirty; then
13 rev+=M
14 fi
15 fi
+46-7
nixos/modules/services/misc/geoipupdate.nix
···23let
4 cfg = config.services.geoipupdate;
05in
6{
7 imports = [
···27 };
2829 settings = lib.mkOption {
00000000030 description = ''
31 <productname>geoipupdate</productname> configuration
32 options. See
33 <link xlink:href="https://github.com/maxmind/geoipupdate/blob/main/doc/GeoIP.conf.md" />
34 for a full list of available options.
000000000035 '';
36 type = lib.types.submodule {
37 freeformType =
···65 };
6667 LicenseKey = lib.mkOption {
68- type = lib.types.path;
69 description = ''
70- A file containing the <productname>MaxMind</productname>
71- license key.
00000072 '';
073 };
7475 DatabaseDirectory = lib.mkOption {
···102 systemd.services.geoipupdate-create-db-dir = {
103 serviceConfig.Type = "oneshot";
104 script = ''
000105 mkdir -p ${cfg.settings.DatabaseDirectory}
106 chmod 0755 ${cfg.settings.DatabaseDirectory}
107 '';
···115 "network-online.target"
116 "nss-lookup.target"
117 ];
0118 wants = [ "network-online.target" ];
119 startAt = cfg.interval;
120 serviceConfig = {
121 ExecStartPre =
122 let
0123 geoipupdateKeyValue = lib.generators.toKeyValue {
124 mkKeyValue = lib.flip lib.generators.mkKeyValueDefault " " rec {
125- mkValueString = v: with builtins;
126 if isInt v then toString v
127 else if isString v then v
128 else if true == v then "1"
129 else if false == v then "0"
130 else if isList v then lib.concatMapStringsSep " " mkValueString v
0131 else throw "unsupported type ${typeOf v}: ${(lib.generators.toPretty {}) v}";
132 };
133 };
00000134135 geoipupdateConf = pkgs.writeText "geoipupdate.conf" (geoipupdateKeyValue cfg.settings);
136137 script = ''
000138 chown geoip "${cfg.settings.DatabaseDirectory}"
139140 cp ${geoipupdateConf} /run/geoipupdate/GeoIP.conf
141- ${pkgs.replace-secret}/bin/replace-secret '${cfg.settings.LicenseKey}' \
142- '${cfg.settings.LicenseKey}' \
143- /run/geoipupdate/GeoIP.conf
144 '';
145 in
146 "+${pkgs.writeShellScript "start-pre-full-privileges" script}";
···23let
4 cfg = config.services.geoipupdate;
5+ inherit (builtins) isAttrs isString isInt isList typeOf hashString;
6in
7{
8 imports = [
···28 };
2930 settings = lib.mkOption {
31+ example = lib.literalExpression ''
32+ {
33+ AccountID = 200001;
34+ DatabaseDirectory = "/var/lib/GeoIP";
35+ LicenseKey = { _secret = "/run/keys/maxmind_license_key"; };
36+ Proxy = "10.0.0.10:8888";
37+ ProxyUserPassword = { _secret = "/run/keys/proxy_pass"; };
38+ }
39+ '';
40 description = ''
41 <productname>geoipupdate</productname> configuration
42 options. See
43 <link xlink:href="https://github.com/maxmind/geoipupdate/blob/main/doc/GeoIP.conf.md" />
44 for a full list of available options.
45+46+ Settings containing secret data should be set to an
47+ attribute set containing the attribute
48+ <literal>_secret</literal> - a string pointing to a file
49+ containing the value the option should be set to. See the
50+ example to get a better picture of this: in the resulting
51+ <filename>GeoIP.conf</filename> file, the
52+ <literal>ProxyUserPassword</literal> key will be set to the
53+ contents of the
54+ <filename>/run/keys/proxy_pass</filename> file.
55 '';
56 type = lib.types.submodule {
57 freeformType =
···85 };
8687 LicenseKey = lib.mkOption {
88+ type = with lib.types; either path (attrsOf path);
89 description = ''
90+ A file containing the
91+ <productname>MaxMind</productname> license key.
92+93+ Always handled as a secret whether the value is
94+ wrapped in a <literal>{ _secret = ...; }</literal>
95+ attrset or not (refer to <xref
96+ linkend="opt-services.geoipupdate.settings" /> for
97+ details).
98 '';
99+ apply = x: if isAttrs x then x else { _secret = x; };
100 };
101102 DatabaseDirectory = lib.mkOption {
···129 systemd.services.geoipupdate-create-db-dir = {
130 serviceConfig.Type = "oneshot";
131 script = ''
132+ set -o errexit -o pipefail -o nounset -o errtrace
133+ shopt -s inherit_errexit
134+135 mkdir -p ${cfg.settings.DatabaseDirectory}
136 chmod 0755 ${cfg.settings.DatabaseDirectory}
137 '';
···145 "network-online.target"
146 "nss-lookup.target"
147 ];
148+ path = [ pkgs.replace-secret ];
149 wants = [ "network-online.target" ];
150 startAt = cfg.interval;
151 serviceConfig = {
152 ExecStartPre =
153 let
154+ isSecret = v: isAttrs v && v ? _secret && isString v._secret;
155 geoipupdateKeyValue = lib.generators.toKeyValue {
156 mkKeyValue = lib.flip lib.generators.mkKeyValueDefault " " rec {
157+ mkValueString = v:
158 if isInt v then toString v
159 else if isString v then v
160 else if true == v then "1"
161 else if false == v then "0"
162 else if isList v then lib.concatMapStringsSep " " mkValueString v
163+ else if isSecret v then hashString "sha256" v._secret
164 else throw "unsupported type ${typeOf v}: ${(lib.generators.toPretty {}) v}";
165 };
166 };
167+ secretPaths = lib.catAttrs "_secret" (lib.collect isSecret cfg.settings);
168+ mkSecretReplacement = file: ''
169+ replace-secret ${lib.escapeShellArgs [ (hashString "sha256" file) file "/run/geoipupdate/GeoIP.conf" ]}
170+ '';
171+ secretReplacements = lib.concatMapStrings mkSecretReplacement secretPaths;
172173 geoipupdateConf = pkgs.writeText "geoipupdate.conf" (geoipupdateKeyValue cfg.settings);
174175 script = ''
176+ set -o errexit -o pipefail -o nounset -o errtrace
177+ shopt -s inherit_errexit
178+179 chown geoip "${cfg.settings.DatabaseDirectory}"
180181 cp ${geoipupdateConf} /run/geoipupdate/GeoIP.conf
182+ ${secretReplacements}
00183 '';
184 in
185 "+${pkgs.writeShellScript "start-pre-full-privileges" script}";
+79-84
nixos/modules/services/monitoring/parsedmarc.nix
···3let
4 cfg = config.services.parsedmarc;
5 opt = options.services.parsedmarc;
6- ini = pkgs.formats.ini {};
0000000000007in
8{
9 options.services.parsedmarc = {
···107 };
108109 settings = lib.mkOption {
000000000000000110 description = ''
111 Configuration parameters to set in
112 <filename>parsedmarc.ini</filename>. For a full list of
113 available parameters, see
114 <link xlink:href="https://domainaware.github.io/parsedmarc/#configuration-file" />.
000000000115 '';
116117 type = lib.types.submodule {
···170 };
171172 password = lib.mkOption {
173- type = with lib.types; nullOr path;
174 default = null;
175 description = ''
176- The path to a file containing the IMAP server password.
000000177 '';
0178 };
179180 watch = lib.mkOption {
···228 };
229230 password = lib.mkOption {
231- type = with lib.types; nullOr path;
232 default = null;
233 description = ''
234- The path to a file containing the SMTP server password.
000000235 '';
0236 };
237238 from = lib.mkOption {
···274 };
275276 password = lib.mkOption {
277- type = with lib.types; nullOr path;
278 default = null;
279 description = ''
280- The path to a file containing the password to use when
281- connecting to Elasticsearch, if required.
000000282 '';
0283 };
284285 ssl = lib.mkOption {
···299 '';
300 };
301 };
302-303- kafka = {
304- hosts = lib.mkOption {
305- default = [];
306- type = with lib.types; listOf str;
307- apply = x: if x == [] then null else lib.concatStringsSep "," x;
308- description = ''
309- A list of Apache Kafka hosts to publish parsed reports
310- to.
311- '';
312- };
313-314- user = lib.mkOption {
315- type = with lib.types; nullOr str;
316- default = null;
317- description = ''
318- Username to use when connecting to Kafka, if
319- required.
320- '';
321- };
322-323- password = lib.mkOption {
324- type = with lib.types; nullOr path;
325- default = null;
326- description = ''
327- The path to a file containing the password to use when
328- connecting to Kafka, if required.
329- '';
330- };
331-332- ssl = lib.mkOption {
333- type = with lib.types; nullOr bool;
334- default = null;
335- description = ''
336- Whether to use an encrypted SSL/TLS connection.
337- '';
338- };
339-340- aggregate_topic = lib.mkOption {
341- type = with lib.types; nullOr str;
342- default = null;
343- example = "aggregate";
344- description = ''
345- The Kafka topic to publish aggregate reports on.
346- '';
347- };
348-349- forensic_topic = lib.mkOption {
350- type = with lib.types; nullOr str;
351- default = null;
352- example = "forensic";
353- description = ''
354- The Kafka topic to publish forensic reports on.
355- '';
356- };
357- };
358-359 };
360361 };
···404 enable = cfg.provision.grafana.datasource || cfg.provision.grafana.dashboard;
405 datasources =
406 let
407- pkgVer = lib.getVersion config.services.elasticsearch.package;
408- esVersion =
409- if lib.versionOlder pkgVer "7" then
410- "60"
411- else if lib.versionOlder pkgVer "8" then
412- "70"
413- else
414- throw "When provisioning parsedmarc grafana datasources: unknown Elasticsearch version.";
415 in
416 lib.mkIf cfg.provision.grafana.datasource [
417 {
418 name = "dmarc-ag";
419 type = "elasticsearch";
420 access = "proxy";
421- url = "localhost:9200";
422 jsonData = {
423 timeField = "date_range";
424 inherit esVersion;
···428 name = "dmarc-fo";
429 type = "elasticsearch";
430 access = "proxy";
431- url = "localhost:9200";
432 jsonData = {
433 timeField = "date_range";
434 inherit esVersion;
···467 # lists, empty attrsets and null. This makes it possible to
468 # list interesting options in `settings` without them always
469 # ending up in the resulting config.
470- filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! builtins.elem v [ null [] {} ])) cfg.settings;
00000471 parsedmarcConfig = ini.generate "parsedmarc.ini" filteredConfig;
472- mkSecretReplacement = file:
473- lib.optionalString (file != null) ''
474- replace-secret '${file}' '${file}' /run/parsedmarc/parsedmarc.ini
475- '';
476 in
477 {
478 wantedBy = [ "multi-user.target" ];
···487 umask u=rwx,g=,o=
488 cp ${parsedmarcConfig} /run/parsedmarc/parsedmarc.ini
489 chown parsedmarc:parsedmarc /run/parsedmarc/parsedmarc.ini
490- ${mkSecretReplacement cfg.settings.smtp.password}
491- ${mkSecretReplacement cfg.settings.imap.password}
492- ${mkSecretReplacement cfg.settings.elasticsearch.password}
493- ${mkSecretReplacement cfg.settings.kafka.password}
494 '' + lib.optionalString cfg.provision.localMail.enable ''
495 openssl rand -hex 64 >/run/parsedmarc/dmarc_user_passwd
496 replace-secret '@imap-password@' '/run/parsedmarc/dmarc_user_passwd' /run/parsedmarc/parsedmarc.ini
···3let
4 cfg = config.services.parsedmarc;
5 opt = options.services.parsedmarc;
6+ isSecret = v: isAttrs v && v ? _secret && isString v._secret;
7+ ini = pkgs.formats.ini {
8+ mkKeyValue = lib.flip lib.generators.mkKeyValueDefault "=" rec {
9+ mkValueString = v:
10+ if isInt v then toString v
11+ else if isString v then v
12+ else if true == v then "True"
13+ else if false == v then "False"
14+ else if isSecret v then hashString "sha256" v._secret
15+ else throw "unsupported type ${typeOf v}: ${(lib.generators.toPretty {}) v}";
16+ };
17+ };
18+ inherit (builtins) elem isAttrs isString isInt isList typeOf hashString;
19in
20{
21 options.services.parsedmarc = {
···119 };
120121 settings = lib.mkOption {
122+ example = lib.literalExpression ''
123+ {
124+ imap = {
125+ host = "imap.example.com";
126+ user = "alice@example.com";
127+ password = { _secret = "/run/keys/imap_password" };
128+ watch = true;
129+ };
130+ splunk_hec = {
131+ url = "https://splunkhec.example.com";
132+ token = { _secret = "/run/keys/splunk_token" };
133+ index = "email";
134+ };
135+ }
136+ '';
137 description = ''
138 Configuration parameters to set in
139 <filename>parsedmarc.ini</filename>. For a full list of
140 available parameters, see
141 <link xlink:href="https://domainaware.github.io/parsedmarc/#configuration-file" />.
142+143+ Settings containing secret data should be set to an attribute
144+ set containing the attribute <literal>_secret</literal> - a
145+ string pointing to a file containing the value the option
146+ should be set to. See the example to get a better picture of
147+ this: in the resulting <filename>parsedmarc.ini</filename>
148+ file, the <literal>splunk_hec.token</literal> key will be set
149+ to the contents of the
150+ <filename>/run/keys/splunk_token</filename> file.
151 '';
152153 type = lib.types.submodule {
···206 };
207208 password = lib.mkOption {
209+ type = with lib.types; nullOr (either path (attrsOf path));
210 default = null;
211 description = ''
212+ The IMAP server password.
213+214+ Always handled as a secret whether the value is
215+ wrapped in a <literal>{ _secret = ...; }</literal>
216+ attrset or not (refer to <xref
217+ linkend="opt-services.parsedmarc.settings" /> for
218+ details).
219 '';
220+ apply = x: if isAttrs x || x == null then x else { _secret = x; };
221 };
222223 watch = lib.mkOption {
···271 };
272273 password = lib.mkOption {
274+ type = with lib.types; nullOr (either path (attrsOf path));
275 default = null;
276 description = ''
277+ The SMTP server password.
278+279+ Always handled as a secret whether the value is
280+ wrapped in a <literal>{ _secret = ...; }</literal>
281+ attrset or not (refer to <xref
282+ linkend="opt-services.parsedmarc.settings" /> for
283+ details).
284 '';
285+ apply = x: if isAttrs x || x == null then x else { _secret = x; };
286 };
287288 from = lib.mkOption {
···324 };
325326 password = lib.mkOption {
327+ type = with lib.types; nullOr (either path (attrsOf path));
328 default = null;
329 description = ''
330+ The password to use when connecting to Elasticsearch,
331+ if required.
332+333+ Always handled as a secret whether the value is
334+ wrapped in a <literal>{ _secret = ...; }</literal>
335+ attrset or not (refer to <xref
336+ linkend="opt-services.parsedmarc.settings" /> for
337+ details).
338 '';
339+ apply = x: if isAttrs x || x == null then x else { _secret = x; };
340 };
341342 ssl = lib.mkOption {
···356 '';
357 };
358 };
000000000000000000000000000000000000000000000000000000000359 };
360361 };
···404 enable = cfg.provision.grafana.datasource || cfg.provision.grafana.dashboard;
405 datasources =
406 let
407+ esVersion = lib.getVersion config.services.elasticsearch.package;
0000000408 in
409 lib.mkIf cfg.provision.grafana.datasource [
410 {
411 name = "dmarc-ag";
412 type = "elasticsearch";
413 access = "proxy";
414+ url = "http://localhost:9200";
415 jsonData = {
416 timeField = "date_range";
417 inherit esVersion;
···421 name = "dmarc-fo";
422 type = "elasticsearch";
423 access = "proxy";
424+ url = "http://localhost:9200";
425 jsonData = {
426 timeField = "date_range";
427 inherit esVersion;
···460 # lists, empty attrsets and null. This makes it possible to
461 # list interesting options in `settings` without them always
462 # ending up in the resulting config.
463+ filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! elem v [ null [] {} ])) cfg.settings;
464+465+ # Extract secrets (attributes set to an attrset with a
466+ # "_secret" key) from the settings and generate the commands
467+ # to run to perform the secret replacements.
468+ secretPaths = lib.catAttrs "_secret" (lib.collect isSecret filteredConfig);
469 parsedmarcConfig = ini.generate "parsedmarc.ini" filteredConfig;
470+ mkSecretReplacement = file: ''
471+ replace-secret ${lib.escapeShellArgs [ (hashString "sha256" file) file "/run/parsedmarc/parsedmarc.ini" ]}
472+ '';
473+ secretReplacements = lib.concatMapStrings mkSecretReplacement secretPaths;
474 in
475 {
476 wantedBy = [ "multi-user.target" ];
···485 umask u=rwx,g=,o=
486 cp ${parsedmarcConfig} /run/parsedmarc/parsedmarc.ini
487 chown parsedmarc:parsedmarc /run/parsedmarc/parsedmarc.ini
488+ ${secretReplacements}
000489 '' + lib.optionalString cfg.provision.localMail.enable ''
490 openssl rand -hex 64 >/run/parsedmarc/dmarc_user_passwd
491 replace-secret '@imap-password@' '/run/parsedmarc/dmarc_user_passwd' /run/parsedmarc/parsedmarc.ini
···45994600 boofuzz= callPackage ../tools/security/boofuzz { };
46014602+ briar-desktop = callPackage ../applications/networking/instant-messengers/briar-desktop { };
4603+4604 bsdbuild = callPackage ../development/tools/misc/bsdbuild { };
46054606 bsdiff = callPackage ../tools/compression/bsdiff { };
···1444214443 inherit (beam.interpreters)
14444 erlang erlangR25 erlangR24 erlangR23 erlangR22 erlangR21
14445+ erlang_odbc erlang_javac erlang_odbc_javac
14446 elixir elixir_1_13 elixir_1_12 elixir_1_11 elixir_1_10 elixir_1_9
14447 elixir_ls;
14448···16138 nwjs-sdk = callPackage ../development/tools/nwjs {
16139 sdk = true;
16140 };
16141+16142+ nrf5-sdk = callPackage ../development/libraries/nrf5-sdk { };
1614316144 nrfutil = callPackage ../development/tools/misc/nrfutil { };
16145···2093120932 sphinx = with python3Packages; toPythonApplication sphinx;
2093320934+ # A variation of sphinx that is only suitable for offline use as it excludes
20935+ # pyopenssl, which is broken on aarch64-darwin.
20936+ # https://github.com/NixOS/nixpkgs/issues/175875
20937+ sphinx_offline =
20938+ if !(stdenv.buildPlatform.isDarwin && stdenv.buildPlatform.isAarch64)
20939+ then sphinx
20940+ else
20941+ sphinx.override (o: {
20942+ requests = pkgsBuildTarget.python3Packages.requests.override (o: {
20943+ urllib3 = pkgsBuildTarget.python3Packages.urllib3.overrideAttrs (o: {
20944+ # urllib3 adds the optional pyopenssl to propagatedBuildInputs
20945+ # pkgs/development/python-modules/urllib3/default.nix
20946+ propagatedBuildInputs = [];
20947+ });
20948+ });
20949+ });
20950+20951 sphinx-autobuild = with python3Packages; toPythonApplication sphinx-autobuild;
2095220953 sphinx-serve = with python3Packages; toPythonApplication sphinx-serve;
···2700827009 # Git with SVN support, but without GUI.
27010 gitSVN = lowPrio (git.override { svnSupport = true; });
27011+27012+ git-autofixup = perlPackages.GitAutofixup;
2701327014 git-doc = lib.addMetaAttrs {
27015 description = "Additional documentation for Git";
-9
pkgs/top-level/beam-packages.nix
···92 odbcSupport = true;
93 };
9495- # Basho fork, using custom builder.
96- erlang_basho_R16B02 =
97- lib.callErlang ../development/interpreters/erlang/R16B02-basho.nix {
98- autoconf = buildPackages.autoconf269;
99- inherit wxSupport;
100- };
101- erlang_basho_R16B02_odbc =
102- erlang_basho_R16B02.override { odbcSupport = true; };
103-104 # Other Beam languages. These are built with `beam.interpreters.erlang`. To
105 # access for example elixir built with different version of Erlang, use
106 # `beam.packages.erlangR24.elixir`.
···92 odbcSupport = true;
93 };
9400000000095 # Other Beam languages. These are built with `beam.interpreters.erlang`. To
96 # access for example elixir built with different version of Erlang, use
97 # `beam.packages.erlangR24.elixir`.
+7-5
pkgs/top-level/haskell-packages.nix
···49 # Use this rather than `rec { ... }` below for sake of overlays.
50 inherit (pkgs.haskell) compiler packages;
510052in {
53 lib = haskellLibUncomposable;
54···97 packages.ghc8102Binary
98 else
99 packages.ghc865Binary;
100- inherit (buildPackages.python3Packages) sphinx;
101 buildTargetLlvmPackages = pkgsBuildTarget.llvmPackages_7;
102 llvmPackages = pkgs.llvmPackages_7;
103 };
···110 packages.ghc8107BinaryMinimal
111 else
112 packages.ghc8107Binary;
113- inherit (buildPackages.python3Packages) sphinx;
114 # Need to use apple's patched xattr until
115 # https://github.com/xattr/xattr/issues/44 and
116 # https://github.com/xattr/xattr/issues/55 are solved.
···126 packages.ghc8107BinaryMinimal
127 else
128 packages.ghc8107Binary;
129- inherit (buildPackages.python3Packages) sphinx;
130 inherit (buildPackages.darwin) autoSignDarwinBinariesHook xattr;
131 buildTargetLlvmPackages = pkgsBuildTarget.llvmPackages_12;
132 llvmPackages = pkgs.llvmPackages_12;
···138 packages.ghc8107BinaryMinimal
139 else
140 packages.ghc8107Binary;
141- inherit (buildPackages.python3Packages) sphinx;
142 # Need to use apple's patched xattr until
143 # https://github.com/xattr/xattr/issues/44 and
144 # https://github.com/xattr/xattr/issues/55 are solved.
···148 };
149 ghcHEAD = callPackage ../development/compilers/ghc/head.nix {
150 bootPkgs = packages.ghc8107Binary;
151- inherit (buildPackages.python3Packages) sphinx;
152 # Need to use apple's patched xattr until
153 # https://github.com/xattr/xattr/issues/44 and
154 # https://github.com/xattr/xattr/issues/55 are solved.
···49 # Use this rather than `rec { ... }` below for sake of overlays.
50 inherit (pkgs.haskell) compiler packages;
5152+ sphinx = buildPackages.sphinx_offline;
53+54in {
55 lib = haskellLibUncomposable;
56···99 packages.ghc8102Binary
100 else
101 packages.ghc865Binary;
102+ inherit sphinx;
103 buildTargetLlvmPackages = pkgsBuildTarget.llvmPackages_7;
104 llvmPackages = pkgs.llvmPackages_7;
105 };
···112 packages.ghc8107BinaryMinimal
113 else
114 packages.ghc8107Binary;
115+ inherit sphinx;
116 # Need to use apple's patched xattr until
117 # https://github.com/xattr/xattr/issues/44 and
118 # https://github.com/xattr/xattr/issues/55 are solved.
···128 packages.ghc8107BinaryMinimal
129 else
130 packages.ghc8107Binary;
131+ inherit sphinx;
132 inherit (buildPackages.darwin) autoSignDarwinBinariesHook xattr;
133 buildTargetLlvmPackages = pkgsBuildTarget.llvmPackages_12;
134 llvmPackages = pkgs.llvmPackages_12;
···140 packages.ghc8107BinaryMinimal
141 else
142 packages.ghc8107Binary;
143+ inherit sphinx;
144 # Need to use apple's patched xattr until
145 # https://github.com/xattr/xattr/issues/44 and
146 # https://github.com/xattr/xattr/issues/55 are solved.
···150 };
151 ghcHEAD = callPackage ../development/compilers/ghc/head.nix {
152 bootPkgs = packages.ghc8107Binary;
153+ inherit sphinx;
154 # Need to use apple's patched xattr until
155 # https://github.com/xattr/xattr/issues/44 and
156 # https://github.com/xattr/xattr/issues/55 are solved.