···1515 # we don't limit this action to only NixOS repo since the checks are cheap and useful developer feedback
1616 steps:
1717 - uses: actions/checkout@v2
1818- - uses: cachix/install-nix-action@v14
1818+ - uses: cachix/install-nix-action@v15
1919 # explicit list of supportedSystems is needed until aarch64-darwin becomes part of the trunk jobset
2020 - run: nix-build pkgs/top-level/release.nix -A tarball.nixpkgs-basic-release-checks --arg supportedSystems '[ "aarch64-darwin" "aarch64-linux" "x86_64-linux" "x86_64-darwin" ]'
+1-1
.github/workflows/editorconfig.yml
···2828 # pull_request_target checks out the base branch by default
2929 ref: refs/pull/${{ github.event.pull_request.number }}/merge
3030 if: env.PR_DIFF
3131- - uses: cachix/install-nix-action@v14
3131+ - uses: cachix/install-nix-action@v15
3232 if: env.PR_DIFF
3333 with:
3434 # nixpkgs commit is pinned so that it doesn't break
+1-1
.github/workflows/manual-nixos.yml
···1818 with:
1919 # pull_request_target checks out the base branch by default
2020 ref: refs/pull/${{ github.event.pull_request.number }}/merge
2121- - uses: cachix/install-nix-action@v14
2121+ - uses: cachix/install-nix-action@v15
2222 with:
2323 # explicitly enable sandbox
2424 extra_nix_config: sandbox = true
+1-1
.github/workflows/manual-nixpkgs.yml
···1818 with:
1919 # pull_request_target checks out the base branch by default
2020 ref: refs/pull/${{ github.event.pull_request.number }}/merge
2121- - uses: cachix/install-nix-action@v14
2121+ - uses: cachix/install-nix-action@v15
2222 with:
2323 # explicitly enable sandbox
2424 extra_nix_config: sandbox = true
+1-1
.github/workflows/nixos-manual.yml
···1919 with:
2020 # pull_request_target checks out the base branch by default
2121 ref: refs/pull/${{ github.event.pull_request.number }}/merge
2222- - uses: cachix/install-nix-action@v14
2222+ - uses: cachix/install-nix-action@v15
2323 - name: Check DocBook files generated from Markdown are consistent
2424 run: |
2525 nixos/doc/manual/md-to-db.sh
···11+{ config, lib, pkgs, ... }:
22+33+with lib;
44+let
55+ cfg = config.services.mjolnir;
66+77+ yamlConfig = {
88+ inherit (cfg) dataPath managementRoom protectedRooms;
99+1010+ accessToken = "@ACCESS_TOKEN@"; # will be replaced in "generateConfig"
1111+ homeserverUrl =
1212+ if cfg.pantalaimon.enable then
1313+ "http://${cfg.pantalaimon.options.listenAddress}:${toString cfg.pantalaimon.options.listenPort}"
1414+ else
1515+ cfg.homeserverUrl;
1616+1717+ pantalaimon = {
1818+ inherit (cfg.pantalaimon) username;
1919+2020+ use = cfg.pantalaimon.enable;
2121+ password = "@PANTALAIMON_PASSWORD@"; # will be replaced in "generateConfig"
2222+ };
2323+ };
2424+2525+ moduleConfigFile = pkgs.writeText "module-config.yaml" (
2626+ generators.toYAML { } (filterAttrs (_: v: v != null)
2727+ (fold recursiveUpdate { } [ yamlConfig cfg.settings ])));
2828+2929+ # these config files will be merged one after the other to build the final config
3030+ configFiles = [
3131+ "${pkgs.mjolnir}/share/mjolnir/config/default.yaml"
3232+ moduleConfigFile
3333+ ];
3434+3535+ # this will generate the default.yaml file with all configFiles as inputs and
3636+ # replace all secret strings using replace-secret
3737+ generateConfig = pkgs.writeShellScript "mjolnir-generate-config" (
3838+ let
3939+ yqEvalStr = concatImapStringsSep " * " (pos: _: "select(fileIndex == ${toString (pos - 1)})") configFiles;
4040+ yqEvalArgs = concatStringsSep " " configFiles;
4141+ in
4242+ ''
4343+ set -euo pipefail
4444+4545+ umask 077
4646+4747+ # mjolnir will try to load a config from "./config/default.yaml" in the working directory
4848+ # -> let's place the generated config there
4949+ mkdir -p ${cfg.dataPath}/config
5050+5151+ # merge all config files into one, overriding settings of the previous one with the next config
5252+ # e.g. "eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' filea.yaml fileb.yaml" will merge filea.yaml with fileb.yaml
5353+ ${pkgs.yq-go}/bin/yq eval-all -P '${yqEvalStr}' ${yqEvalArgs} > ${cfg.dataPath}/config/default.yaml
5454+5555+ ${optionalString (cfg.accessTokenFile != null) ''
5656+ ${pkgs.replace-secret}/bin/replace-secret '@ACCESS_TOKEN@' '${cfg.accessTokenFile}' ${cfg.dataPath}/config/default.yaml
5757+ ''}
5858+ ${optionalString (cfg.pantalaimon.passwordFile != null) ''
5959+ ${pkgs.replace-secret}/bin/replace-secret '@PANTALAIMON_PASSWORD@' '${cfg.pantalaimon.passwordFile}' ${cfg.dataPath}/config/default.yaml
6060+ ''}
6161+ ''
6262+ );
6363+in
6464+{
6565+ options.services.mjolnir = {
6666+ enable = mkEnableOption "Mjolnir, a moderation tool for Matrix";
6767+6868+ homeserverUrl = mkOption {
6969+ type = types.str;
7070+ default = "https://matrix.org";
7171+ description = ''
7272+ Where the homeserver is located (client-server URL).
7373+7474+ If <literal>pantalaimon.enable</literal> is <literal>true</literal>, this option will become the homeserver to which <literal>pantalaimon</literal> connects.
7575+ The listen address of <literal>pantalaimon</literal> will then become the <literal>homeserverUrl</literal> of <literal>mjolnir</literal>.
7676+ '';
7777+ };
7878+7979+ accessTokenFile = mkOption {
8080+ type = with types; nullOr path;
8181+ default = null;
8282+ description = ''
8383+ File containing the matrix access token for the <literal>mjolnir</literal> user.
8484+ '';
8585+ };
8686+8787+ pantalaimon = mkOption {
8888+ description = ''
8989+ <literal>pantalaimon</literal> options (enables E2E Encryption support).
9090+9191+ This will create a <literal>pantalaimon</literal> instance with the name "mjolnir".
9292+ '';
9393+ default = { };
9494+ type = types.submodule {
9595+ options = {
9696+ enable = mkEnableOption ''
9797+ If true, accessToken is ignored and the username/password below will be
9898+ used instead. The access token of the bot will be stored in the dataPath.
9999+ '';
100100+101101+ username = mkOption {
102102+ type = types.str;
103103+ description = "The username to login with.";
104104+ };
105105+106106+ passwordFile = mkOption {
107107+ type = with types; nullOr path;
108108+ default = null;
109109+ description = ''
110110+ File containing the matrix password for the <literal>mjolnir</literal> user.
111111+ '';
112112+ };
113113+114114+ options = mkOption {
115115+ type = types.submodule (import ./pantalaimon-options.nix);
116116+ default = { };
117117+ description = ''
118118+ passthrough additional options to the <literal>pantalaimon</literal> service.
119119+ '';
120120+ };
121121+ };
122122+ };
123123+ };
124124+125125+ dataPath = mkOption {
126126+ type = types.path;
127127+ default = "/var/lib/mjolnir";
128128+ description = ''
129129+ The directory the bot should store various bits of information in.
130130+ '';
131131+ };
132132+133133+ managementRoom = mkOption {
134134+ type = types.str;
135135+ default = "#moderators:example.org";
136136+ description = ''
137137+ The room ID where people can use the bot. The bot has no access controls, so
138138+ anyone in this room can use the bot - secure your room!
139139+ This should be a room alias or room ID - not a matrix.to URL.
140140+ Note: <literal>mjolnir</literal> is fairly verbose - expect a lot of messages from it.
141141+ '';
142142+ };
143143+144144+ protectedRooms = mkOption {
145145+ type = types.listOf types.str;
146146+ default = [ ];
147147+ example = literalExpression ''
148148+ [
149149+ "https://matrix.to/#/#yourroom:example.org"
150150+ "https://matrix.to/#/#anotherroom:example.org"
151151+ ]
152152+ '';
153153+ description = ''
154154+ A list of rooms to protect (matrix.to URLs).
155155+ '';
156156+ };
157157+158158+ settings = mkOption {
159159+ default = { };
160160+ type = (pkgs.formats.yaml { }).type;
161161+ example = literalExpression ''
162162+ {
163163+ autojoinOnlyIfManager = true;
164164+ automaticallyRedactForReasons = [ "spam" "advertising" ];
165165+ }
166166+ '';
167167+ description = ''
168168+ Additional settings (see <link xlink:href="https://github.com/matrix-org/mjolnir/blob/main/config/default.yaml">mjolnir default config</link> for available settings). These settings will override settings made by the module config.
169169+ '';
170170+ };
171171+ };
172172+173173+ config = mkIf config.services.mjolnir.enable {
174174+ assertions = [
175175+ {
176176+ assertion = !(cfg.pantalaimon.enable && cfg.pantalaimon.passwordFile == null);
177177+ message = "Specify pantalaimon.passwordFile";
178178+ }
179179+ {
180180+ assertion = !(cfg.pantalaimon.enable && cfg.accessTokenFile != null);
181181+ message = "Do not specify accessTokenFile when using pantalaimon";
182182+ }
183183+ {
184184+ assertion = !(!cfg.pantalaimon.enable && cfg.accessTokenFile == null);
185185+ message = "Specify accessTokenFile when not using pantalaimon";
186186+ }
187187+ ];
188188+189189+ services.pantalaimon-headless.instances."mjolnir" = mkIf cfg.pantalaimon.enable
190190+ {
191191+ homeserver = cfg.homeserverUrl;
192192+ } // cfg.pantalaimon.options;
193193+194194+ systemd.services.mjolnir = {
195195+ description = "mjolnir - a moderation tool for Matrix";
196196+ wants = [ "network-online.target" ] ++ optionals (cfg.pantalaimon.enable) [ "pantalaimon-mjolnir.service" ];
197197+ after = [ "network-online.target" ] ++ optionals (cfg.pantalaimon.enable) [ "pantalaimon-mjolnir.service" ];
198198+ wantedBy = [ "multi-user.target" ];
199199+200200+ serviceConfig = {
201201+ ExecStart = ''${pkgs.mjolnir}/bin/mjolnir'';
202202+ ExecStartPre = [ generateConfig ];
203203+ WorkingDirectory = cfg.dataPath;
204204+ StateDirectory = "mjolnir";
205205+ StateDirectoryMode = "0700";
206206+ ProtectSystem = "strict";
207207+ ProtectHome = true;
208208+ PrivateTmp = true;
209209+ NoNewPrivileges = true;
210210+ PrivateDevices = true;
211211+ User = "mjolnir";
212212+ Restart = "on-failure";
213213+214214+ /* TODO: wait for #102397 to be resolved. Then load secrets from $CREDENTIALS_DIRECTORY+"/NAME"
215215+ DynamicUser = true;
216216+ LoadCredential = [] ++
217217+ optionals (cfg.accessTokenFile != null) [
218218+ "access_token:${cfg.accessTokenFile}"
219219+ ] ++
220220+ optionals (cfg.pantalaimon.passwordFile != null) [
221221+ "pantalaimon_password:${cfg.pantalaimon.passwordFile}"
222222+ ];
223223+ */
224224+ };
225225+ };
226226+227227+ users = {
228228+ users.mjolnir = {
229229+ group = "mjolnir";
230230+ isSystemUser = true;
231231+ };
232232+ groups.mjolnir = { };
233233+ };
234234+ };
235235+236236+ meta = {
237237+ doc = ./mjolnir.xml;
238238+ maintainers = with maintainers; [ jojosch ];
239239+ };
240240+}
+134
nixos/modules/services/matrix/mjolnir.xml
···11+<chapter xmlns="http://docbook.org/ns/docbook"
22+ xmlns:xlink="http://www.w3.org/1999/xlink"
33+ xmlns:xi="http://www.w3.org/2001/XInclude"
44+ version="5.0"
55+ xml:id="module-services-mjolnir">
66+ <title>Mjolnir (Matrix Moderation Tool)</title>
77+ <para>
88+ This chapter will show you how to set up your own, self-hosted
99+ <link xlink:href="https://github.com/matrix-org/mjolnir">Mjolnir</link>
1010+ instance.
1111+ </para>
1212+ <para>
1313+ As an all-in-one moderation tool, it can protect your server from
1414+ malicious invites, spam messages, and whatever else you don't want.
1515+ In addition to server-level protection, Mjolnir is great for communities
1616+ wanting to protect their rooms without having to use their personal
1717+ accounts for moderation.
1818+ </para>
1919+ <para>
2020+ The bot by default includes support for bans, redactions, anti-spam,
2121+ server ACLs, room directory changes, room alias transfers, account
2222+ deactivation, room shutdown, and more.
2323+ </para>
2424+ <para>
2525+ See the <link xlink:href="https://github.com/matrix-org/mjolnir#readme">README</link>
2626+ page and the <link xlink:href="https://github.com/matrix-org/mjolnir/blob/main/docs/moderators.md">Moderator's guide</link>
2727+ for additional instructions on how to setup and use Mjolnir.
2828+ </para>
2929+ <para>
3030+ For <link linkend="opt-services.mjolnir.settings">additional settings</link>
3131+ see <link xlink:href="https://github.com/matrix-org/mjolnir/blob/main/config/default.yaml">the default configuration</link>.
3232+ </para>
3333+ <section xml:id="module-services-mjolnir-setup">
3434+ <title>Mjolnir Setup</title>
3535+ <para>
3636+ First create a new Room which will be used as a management room for Mjolnir. In
3737+ this room, Mjolnir will log possible errors and debugging information. You'll
3838+ need to set this Room-ID in <link linkend="opt-services.mjolnir.managementRoom">services.mjolnir.managementRoom</link>.
3939+ </para>
4040+ <para>
4141+ Next, create a new user for Mjolnir on your homeserver, if not present already.
4242+ </para>
4343+ <para>
4444+ The Mjolnir Matrix user expects to be free of any rate limiting.
4545+ See <link xlink:href="https://github.com/matrix-org/synapse/issues/6286">Synapse #6286</link>
4646+ for an example on how to achieve this.
4747+ </para>
4848+ <para>
4949+ If you want Mjolnir to be able to deactivate users, move room aliases, shutdown rooms, etc.
5050+ you'll need to make the Mjolnir user a Matrix server admin.
5151+ </para>
5252+ <para>
5353+ Now invite the Mjolnir user to the management room.
5454+ </para>
5555+ <para>
5656+ It is recommended to use <link xlink:href="https://github.com/matrix-org/pantalaimon">Pantalaimon</link>,
5757+ so your management room can be encrypted. This also applies if you are looking to moderate an encrypted room.
5858+ </para>
5959+ <para>
6060+ To enable the Pantalaimon E2E Proxy for mjolnir, enable
6161+ <link linkend="opt-services.mjolnir.pantalaimon.enable">services.mjolnir.pantalaimon</link>. This will
6262+ autoconfigure a new Pantalaimon instance, which will connect to the homeserver
6363+ set in <link linkend="opt-services.mjolnir.homeserverUrl">services.mjolnir.homeserverUrl</link> and Mjolnir itself
6464+ will be configured to connect to the new Pantalaimon instance.
6565+ </para>
6666+<programlisting>
6767+{
6868+ services.mjolnir = {
6969+ enable = true;
7070+ <link linkend="opt-services.mjolnir.homeserverUrl">homeserverUrl</link> = "https://matrix.domain.tld";
7171+ <link linkend="opt-services.mjolnir.pantalaimon">pantalaimon</link> = {
7272+ <link linkend="opt-services.mjolnir.pantalaimon.enable">enable</link> = true;
7373+ <link linkend="opt-services.mjolnir.pantalaimon.username">username</link> = "mjolnir";
7474+ <link linkend="opt-services.mjolnir.pantalaimon.passwordFile">passwordFile</link> = "/run/secrets/mjolnir-password";
7575+ };
7676+ <link linkend="opt-services.mjolnir.protectedRooms">protectedRooms</link> = [
7777+ "https://matrix.to/#/!xxx:domain.tld"
7878+ ];
7979+ <link linkend="opt-services.mjolnir.managementRoom">managementRoom</link> = "!yyy:domain.tld";
8080+ };
8181+}
8282+</programlisting>
8383+ <section xml:id="module-services-mjolnir-setup-ems">
8484+ <title>Element Matrix Services (EMS)</title>
8585+ <para>
8686+ If you are using a managed <link xlink:href="https://ems.element.io/">"Element Matrix Services (EMS)"</link>
8787+ server, you will need to consent to the terms and conditions. Upon startup, an error
8888+ log entry with a URL to the consent page will be generated.
8989+ </para>
9090+ </section>
9191+ </section>
9292+9393+ <section xml:id="module-services-mjolnir-matrix-synapse-antispam">
9494+ <title>Synapse Antispam Module</title>
9595+ <para>
9696+ A Synapse module is also available to apply the same rulesets the bot
9797+ uses across an entire homeserver.
9898+ </para>
9999+ <para>
100100+ To use the Antispam Module, add <package>matrix-synapse-plugins.matrix-synapse-mjolnir-antispam</package>
101101+ to the Synapse plugin list and enable the <literal>mjolnir.AntiSpam</literal> module.
102102+ </para>
103103+<programlisting>
104104+{
105105+ services.matrix-synapse = {
106106+ plugins = with pkgs; [
107107+ matrix-synapse-plugins.matrix-synapse-mjolnir-antispam
108108+ ];
109109+ extraConfig = ''
110110+ modules:
111111+ - module: mjolnir.AntiSpam
112112+ config:
113113+ # Prevent servers/users in the ban lists from inviting users on this
114114+ # server to rooms. Default true.
115115+ block_invites: true
116116+ # Flag messages sent by servers/users in the ban lists as spam. Currently
117117+ # this means that spammy messages will appear as empty to users. Default
118118+ # false.
119119+ block_messages: false
120120+ # Remove users from the user directory search by filtering matrix IDs and
121121+ # display names by the entries in the user ban list. Default false.
122122+ block_usernames: false
123123+ # The room IDs of the ban lists to honour. Unlike other parts of Mjolnir,
124124+ # this list cannot be room aliases or permalinks. This server is expected
125125+ # to already be joined to the room - Mjolnir will not automatically join
126126+ # these rooms.
127127+ ban_lists:
128128+ - "!roomid:example.org"
129129+ '';
130130+ };
131131+}
132132+</programlisting>
133133+ </section>
134134+</chapter>
···11+{ config, lib, name, ... }:
22+33+with lib;
44+{
55+ options = {
66+ dataPath = mkOption {
77+ type = types.path;
88+ default = "/var/lib/pantalaimon-${name}";
99+ description = ''
1010+ The directory where <literal>pantalaimon</literal> should store its state such as the database file.
1111+ '';
1212+ };
1313+1414+ logLevel = mkOption {
1515+ type = types.enum [ "info" "warning" "error" "debug" ];
1616+ default = "warning";
1717+ description = ''
1818+ Set the log level of the daemon.
1919+ '';
2020+ };
2121+2222+ homeserver = mkOption {
2323+ type = types.str;
2424+ example = "https://matrix.org";
2525+ description = ''
2626+ The URI of the homeserver that the <literal>pantalaimon</literal> proxy should
2727+ forward requests to, without the matrix API path but including
2828+ the http(s) schema.
2929+ '';
3030+ };
3131+3232+ ssl = mkOption {
3333+ type = types.bool;
3434+ default = true;
3535+ description = ''
3636+ Whether or not SSL verification should be enabled for outgoing
3737+ connections to the homeserver.
3838+ '';
3939+ };
4040+4141+ listenAddress = mkOption {
4242+ type = types.str;
4343+ default = "localhost";
4444+ description = ''
4545+ The address where the daemon will listen to client connections
4646+ for this homeserver.
4747+ '';
4848+ };
4949+5050+ listenPort = mkOption {
5151+ type = types.port;
5252+ default = 8009;
5353+ description = ''
5454+ The port where the daemon will listen to client connections for
5555+ this homeserver. Note that the listen address/port combination
5656+ needs to be unique between different homeservers.
5757+ '';
5858+ };
5959+6060+ extraSettings = mkOption {
6161+ type = types.attrs;
6262+ default = { };
6363+ description = ''
6464+ Extra configuration options. See
6565+ <link xlink:href="https://github.com/matrix-org/pantalaimon/blob/master/docs/man/pantalaimon.5.md">pantalaimon(5)</link>
6666+ for available options.
6767+ '';
6868+ };
6969+ };
7070+}
···2233python3Packages.buildPythonPackage rec {
44 pname = "heisenbridge";
55- version = "1.5.0";
55+ version = "1.6.0";
6677 # Use the release tarball because it has the version set correctly using the
88 # version.txt file.
99 src = fetchurl {
1010 url = "https://github.com/hifi/heisenbridge/releases/download/v${version}/heisenbridge-${version}.tar.gz";
1111- sha256 = "sha256-hg0PnWbec/iQbv4eRVy6JDt/OJ+EOzN+o6VrUGL4YtE=";
1111+ sha256 = "sha256-NhHMReY48lg1FhJlCRjRiSpy+9bDLtIV+j+zX8GZcL4=";
1212 };
13131414 propagatedBuildInputs = with python3Packages; [
+86
pkgs/servers/mjolnir/default.nix
···11+{ lib
22+, nixosTests
33+, stdenv
44+, fetchFromGitHub
55+, makeWrapper
66+, nodejs
77+, pkgs
88+}:
99+1010+stdenv.mkDerivation rec {
1111+ pname = "mjolnir";
1212+ version = "1.1.20";
1313+1414+ src = fetchFromGitHub {
1515+ owner = "matrix-org";
1616+ repo = "mjolnir";
1717+ rev = "v${version}";
1818+ sha256 = "yfMBnNriSpwitR4u664iz+8uWp/3iSTymyFajMBP5xg=";
1919+ };
2020+2121+ nativeBuildInputs = [
2222+ nodejs
2323+ makeWrapper
2424+ ];
2525+2626+ buildPhase =
2727+ let
2828+ nodeDependencies = ((import ./node-composition.nix {
2929+ inherit pkgs nodejs;
3030+ inherit (stdenv.hostPlatform) system;
3131+ }).nodeDependencies.override (old: {
3232+ # access to path '/nix/store/...-source' is forbidden in restricted mode
3333+ src = src;
3434+ dontNpmInstall = true;
3535+ }));
3636+ in
3737+ ''
3838+ runHook preBuild
3939+4040+ ln -s ${nodeDependencies}/lib/node_modules .
4141+ export PATH="${nodeDependencies}/bin:$PATH"
4242+ npm run build
4343+4444+ runHook postBuild
4545+ '';
4646+4747+ installPhase = ''
4848+ runHook preInstall
4949+5050+ mkdir -p $out/share
5151+ cp -a . $out/share/mjolnir
5252+5353+ makeWrapper ${nodejs}/bin/node $out/bin/mjolnir \
5454+ --add-flags $out/share/mjolnir/lib/index.js
5555+5656+ runHook postInstall
5757+ '';
5858+5959+ passthru = {
6060+ tests = {
6161+ inherit (nixosTests) mjolnir;
6262+ };
6363+ updateScript = ./update.sh;
6464+ };
6565+6666+ meta = with lib; {
6767+ description = "A moderation tool for Matrix";
6868+ homepage = "https://github.com/matrix-org/mjolnir";
6969+ longDescription = ''
7070+ As an all-in-one moderation tool, it can protect your server from
7171+ malicious invites, spam messages, and whatever else you don't want.
7272+ In addition to server-level protection, Mjolnir is great for communities
7373+ wanting to protect their rooms without having to use their personal
7474+ accounts for moderation.
7575+7676+ The bot by default includes support for bans, redactions, anti-spam,
7777+ server ACLs, room directory changes, room alias transfers, account
7878+ deactivation, room shutdown, and more.
7979+8080+ A Synapse module is also available to apply the same rulesets the bot
8181+ uses across an entire homeserver.
8282+ '';
8383+ license = licenses.asl20;
8484+ maintainers = with maintainers; [ jojosch ];
8585+ };
8686+}
+17
pkgs/servers/mjolnir/node-composition.nix
···11+# This file has been generated by node2nix 1.9.0. Do not edit!
22+33+{pkgs ? import <nixpkgs> {
44+ inherit system;
55+ }, system ? builtins.currentSystem, nodejs ? pkgs."nodejs-12_x"}:
66+77+let
88+ nodeEnv = import ./node-env.nix {
99+ inherit (pkgs) stdenv lib python2 runCommand writeTextFile;
1010+ inherit pkgs nodejs;
1111+ libtool = if pkgs.stdenv.isDarwin then pkgs.darwin.cctools else null;
1212+ };
1313+in
1414+import ./node-deps.nix {
1515+ inherit (pkgs) fetchurl nix-gitignore stdenv lib fetchgit;
1616+ inherit nodeEnv;
1717+}
···6677# Version of Pulumi from
88# https://www.pulumi.com/docs/get-started/install/versions/
99-VERSION="3.17.0"
99+VERSION="3.17.1"
10101111# A hashmap containing a plugin's name and it's respective repository inside
1212# Pulumi's Github organization