From 2e8711a58820fa84b4a14c6b6e754a614f22bc0e Mon Sep 17 00:00:00 2001 From: Quentin Aristote Date: Sun, 13 Nov 2022 21:21:00 +0100 Subject: use flakes --- tests/configuration.nix | 41 +++++++++++++++++++++++++++++++++++++++++ tests/run-tests.sh | 2 +- tests/vm.nix | 41 ----------------------------------------- 3 files changed, 42 insertions(+), 42 deletions(-) create mode 100644 tests/configuration.nix delete mode 100644 tests/vm.nix (limited to 'tests') diff --git a/tests/configuration.nix b/tests/configuration.nix new file mode 100644 index 0000000..e878c12 --- /dev/null +++ b/tests/configuration.nix @@ -0,0 +1,41 @@ +{ config, lib, ... }: + +let + nginxPorts = lib.concatLists + (lib.mapAttrsToList (_: cfg: (builtins.map (x: x.port) cfg.listen)) + config.services.nginx.virtualHosts); + nginxMakeLocal = port: { + listen = lib.mkForce [{ + inherit port; + addr = "0.0.0.0"; + }]; + forceSSL = lib.mkForce false; + enableACME = lib.mkForce false; + }; +in { + imports = [ ../configuration.nix ]; + + networking = lib.mkForce { + domain = "aristote.vm"; + + interfaces = { }; + defaultGateway = null; + nameservers = [ ]; + + firewall = { allowedTCPPorts = nginxPorts; }; + }; + + services.filtron.rules = lib.mkForce [ ]; + + services.nginx.virtualHosts = { + quentin = nginxMakeLocal 8080; + searx = nginxMakeLocal 8081; + money = nginxMakeLocal 8082; + rss = nginxMakeLocal 8083; + webkeydirectory = nginxMakeLocal 8084; + }; + + environment.etc."searx/secrets".text = '' + SECRET_KEY=secret_key + ''; +} diff --git a/tests/run-tests.sh b/tests/run-tests.sh index a55e282..b74a6fe 100755 --- a/tests/run-tests.sh +++ b/tests/run-tests.sh @@ -8,7 +8,7 @@ function cleanup { } sudo nixos-container stop hermes || exit 2 -sudo nixos-container update hermes --config-file ./vm.nix || exit 2 +sudo nixos-container update hermes --flake ..#hermes-test || exit 2 echo Starting container ... sudo nixos-container start hermes || exit 2 diff --git a/tests/vm.nix b/tests/vm.nix deleted file mode 100644 index e878c12..0000000 --- a/tests/vm.nix +++ /dev/null @@ -1,41 +0,0 @@ -{ config, lib, ... }: - -let - nginxPorts = lib.concatLists - (lib.mapAttrsToList (_: cfg: (builtins.map (x: x.port) cfg.listen)) - config.services.nginx.virtualHosts); - nginxMakeLocal = port: { - listen = lib.mkForce [{ - inherit port; - addr = "0.0.0.0"; - }]; - forceSSL = lib.mkForce false; - enableACME = lib.mkForce false; - }; -in { - imports = [ ../configuration.nix ]; - - networking = lib.mkForce { - domain = "aristote.vm"; - - interfaces = { }; - defaultGateway = null; - nameservers = [ ]; - - firewall = { allowedTCPPorts = nginxPorts; }; - }; - - services.filtron.rules = lib.mkForce [ ]; - - services.nginx.virtualHosts = { - quentin = nginxMakeLocal 8080; - searx = nginxMakeLocal 8081; - money = nginxMakeLocal 8082; - rss = nginxMakeLocal 8083; - webkeydirectory = nginxMakeLocal 8084; - }; - - environment.etc."searx/secrets".text = '' - SECRET_KEY=secret_key - ''; -} -- cgit v1.3.1 From ab779a7476acc3a5b6dc350badd67f72f9402d6e Mon Sep 17 00:00:00 2001 From: Quentin Aristote Date: Sun, 13 Nov 2022 21:55:15 +0100 Subject: remove hardware configuration from test config --- config/default.nix | 1 - config/hardware-configuration.nix | 27 --------------------------- flake.nix | 2 +- hardware-configuration.nix | 27 +++++++++++++++++++++++++++ tests/configuration.nix | 4 +++- 5 files changed, 31 insertions(+), 30 deletions(-) delete mode 100644 config/hardware-configuration.nix create mode 100644 hardware-configuration.nix (limited to 'tests') diff --git a/config/default.nix b/config/default.nix index 9cb4668..b35ce72 100644 --- a/config/default.nix +++ b/config/default.nix @@ -6,7 +6,6 @@ (modulesPath + "/profiles/minimal.nix") ./boot.nix ./environment.nix - ./hardware-configuration.nix ./networking.nix ./services ./users.nix diff --git a/config/hardware-configuration.nix b/config/hardware-configuration.nix deleted file mode 100644 index 1b79e6f..0000000 --- a/config/hardware-configuration.nix +++ /dev/null @@ -1,27 +0,0 @@ -{ pkgs, lib, modulesPath, ... }: - -{ - imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; - - boot.initrd.availableKernelModules = - [ "ata_piix" "uhci_hcd" "virtio_pci" "sr_mod" "virtio_blk" ]; - boot.initrd.kernelModules = [ ]; - boot.kernelModules = [ "kvm-amd" ]; - boot.extraModulePackages = [ ]; - - fileSystems."/" = { - device = "/dev/disk/by-uuid/2b302948-5608-41c6-b54c-1c0e39ff6a58"; - fsType = "ext4"; - }; - - boot.initrd.luks.devices."root".device = - "/dev/disk/by-uuid/eaec758b-ba22-42ab-8992-e765cec9be55"; - - fileSystems."/boot" = { - device = "/dev/disk/by-uuid/74d78eba-c29a-4724-8fb7-624e0a03faa5"; - fsType = "ext4"; - }; - - swapDevices = [{ device = "/swap"; }]; - -} diff --git a/flake.nix b/flake.nix index d02d308..6ca1663 100644 --- a/flake.nix +++ b/flake.nix @@ -9,7 +9,7 @@ hermes = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; specialArgs = attrs; - modules = [ ./configuration.nix ]; + modules = [ ./configuration.nix ./hardware-configuration.nix ]; }; hermes-test = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; diff --git a/hardware-configuration.nix b/hardware-configuration.nix new file mode 100644 index 0000000..1b79e6f --- /dev/null +++ b/hardware-configuration.nix @@ -0,0 +1,27 @@ +{ pkgs, lib, modulesPath, ... }: + +{ + imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; + + boot.initrd.availableKernelModules = + [ "ata_piix" "uhci_hcd" "virtio_pci" "sr_mod" "virtio_blk" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-amd" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = { + device = "/dev/disk/by-uuid/2b302948-5608-41c6-b54c-1c0e39ff6a58"; + fsType = "ext4"; + }; + + boot.initrd.luks.devices."root".device = + "/dev/disk/by-uuid/eaec758b-ba22-42ab-8992-e765cec9be55"; + + fileSystems."/boot" = { + device = "/dev/disk/by-uuid/74d78eba-c29a-4724-8fb7-624e0a03faa5"; + fsType = "ext4"; + }; + + swapDevices = [{ device = "/swap"; }]; + +} diff --git a/tests/configuration.nix b/tests/configuration.nix index e878c12..73c4bfa 100644 --- a/tests/configuration.nix +++ b/tests/configuration.nix @@ -1,4 +1,4 @@ -{ config, lib, ... }: +{ config, lib, modulesPath, ... }: let nginxPorts = lib.concatLists @@ -15,6 +15,8 @@ let in { imports = [ ../configuration.nix ]; + boot.isContainer = true; + networking = lib.mkForce { domain = "aristote.vm"; -- cgit v1.3.1 From 2ceb13adc217d5e909f40b66c40d418d1b03d4fd Mon Sep 17 00:00:00 2001 From: Quentin Aristote Date: Sun, 13 Nov 2022 22:58:15 +0100 Subject: tests: fix container update --- tests/run-tests.sh | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tests') diff --git a/tests/run-tests.sh b/tests/run-tests.sh index b74a6fe..92acdc8 100755 --- a/tests/run-tests.sh +++ b/tests/run-tests.sh @@ -7,7 +7,9 @@ function cleanup { sudo nixos-container stop hermes } +echo Stopping container for update ... sudo nixos-container stop hermes || exit 2 +echo Updating container ... sudo nixos-container update hermes --flake ..#hermes-test || exit 2 echo Starting container ... sudo nixos-container start hermes || exit 2 -- cgit v1.3.1 From 234e37175bedd061252a311cc3d08f4898132aae Mon Sep 17 00:00:00 2001 From: Quentin Aristote Date: Sun, 13 Nov 2022 22:59:21 +0100 Subject: flake: interface modules and overlays --- configuration.nix | 10 ---------- flake.lock | 32 +++++++++++++++++++------------- flake.nix | 44 ++++++++++++++++++++++++++++++++------------ tests/configuration.nix | 2 +- 4 files changed, 52 insertions(+), 36 deletions(-) delete mode 100644 configuration.nix (limited to 'tests') diff --git a/configuration.nix b/configuration.nix deleted file mode 100644 index 958a63c..0000000 --- a/configuration.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ ... }: - -{ - imports = [ ./config ./modules ]; - - nixpkgs = { - overlays = - [ (final: prev: { personal = import ./pkgs { pkgs = final; }; }) ]; - }; -} diff --git a/flake.lock b/flake.lock index eb376cd..7eee262 100644 --- a/flake.lock +++ b/flake.lock @@ -3,18 +3,20 @@ "data": { "inputs": { "flake-utils": [ - "personal-webpage" + "personal-webpage", + "flake-utils" ], "nixpkgs": [ - "personal-webpage" + "personal-webpage", + "nixpkgs" ] }, "locked": { - "lastModified": 1668340813, - "narHash": "sha256-S3rKgiJqBGsH69zU/A3tlogMC/jpICHtSAzd/CBTVUI=", + "lastModified": 1668375475, + "narHash": "sha256-rp+tPVJkm8QWG48MBhk/+qblvYOmSpLamfAZkhMnyKw=", "owner": "qaristote", "repo": "info", - "rev": "946290203abdc6075b01a40672509bbaabd260d5", + "rev": "5a8c11a5fae47a8eaad7a90ac3a70020243ceb48", "type": "github" }, "original": { @@ -33,9 +35,8 @@ "type": "github" }, "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" + "id": "flake-utils", + "type": "indirect" } }, "nixpkgs": { @@ -55,15 +56,19 @@ "personal-webpage": { "inputs": { "data": "data", - "flake-utils": "flake-utils", - "nixpkgs": [] + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] }, "locked": { - "lastModified": 1668369630, - "narHash": "sha256-aaXUpBgOhMjDYDSsnGs28MExA4o0cXn7tV5ndfxpikU=", + "lastModified": 1668375580, + "narHash": "sha256-eviU7Gx1Y1yTKsWdKQJ0fI/gA/L3MtDp5ZLbleSthzk=", "owner": "qaristote", "repo": "webpage", - "rev": "b0cb171f641dca185cad98506d6c0e104c67dab0", + "rev": "19a0c33863da3223145e4e1c1315d5332ff53df5", "type": "github" }, "original": { @@ -74,6 +79,7 @@ }, "root": { "inputs": { + "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", "personal-webpage": "personal-webpage" } diff --git a/flake.nix b/flake.nix index 6ca1663..ac3ee30 100644 --- a/flake.nix +++ b/flake.nix @@ -1,21 +1,41 @@ { - inputs.personal-webpage = { - url = "github:qaristote/webpage"; - inputs.nixpkgs.follows = ""; + inputs = { + personal-webpage = { + url = "github:qaristote/webpage"; + inputs = { + nixpkgs.follows = "/nixpkgs"; + flake-utils.follows = "/flake-utils"; + }; + }; }; - outputs = { self, nixpkgs, ... }@attrs: { - nixosConfigurations = { - hermes = nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; - specialArgs = attrs; - modules = [ ./configuration.nix ./hardware-configuration.nix ]; + outputs = { self, nixpkgs, personal-webpage, flake-utils, ... }@attrs: + flake-utils.lib.eachDefaultSystem (system: { + overlays.default = final: prev: { + personal = import ./pkgs { pkgs = final; } // { + webpage = personal-webpage.defaultPackage."${system}"; + }; }; - hermes-test = nixpkgs.lib.nixosSystem { + }) // { + nixosModules.default = import ./modules; + nixosConfigurations = let system = "x86_64-linux"; specialArgs = attrs; - modules = [ ./tests/configuration.nix ]; + commonModules = [ + self.nixosModules.default + ({ ... }: { + nixpkgs.overlays = [ self.overlays."${system}".default ]; + }) + ]; + in { + hermes = nixpkgs.lib.nixosSystem { + inherit system specialArgs; + modules = commonModules ++ [ ./config ./hardware-configuration.nix ]; + }; + hermes-test = nixpkgs.lib.nixosSystem { + inherit system specialArgs; + modules = commonModules ++ [ ./tests/configuration.nix ]; + }; }; }; - }; } diff --git a/tests/configuration.nix b/tests/configuration.nix index 73c4bfa..37b043c 100644 --- a/tests/configuration.nix +++ b/tests/configuration.nix @@ -13,7 +13,7 @@ let enableACME = lib.mkForce false; }; in { - imports = [ ../configuration.nix ]; + imports = [ ../config ]; boot.isContainer = true; -- cgit v1.3.1 From 4a24f168e3fb44073237d9aca95a97e77bdefa3c Mon Sep 17 00:00:00 2001 From: Quentin Aristote Date: Sun, 27 Nov 2022 17:41:34 +0100 Subject: services: web: rss: make configuration into a module --- config/services/web/rss/default.nix | 53 ++++++++++++++++++--------- modules/default.nix | 1 + modules/rss-bridge.nix | 71 +++++++++++++++++++++++++++++++++++++ tests/configuration.nix | 2 ++ 4 files changed, 111 insertions(+), 16 deletions(-) create mode 100644 modules/rss-bridge.nix (limited to 'tests') diff --git a/config/services/web/rss/default.nix b/config/services/web/rss/default.nix index 18a6763..acc9331 100644 --- a/config/services/web/rss/default.nix +++ b/config/services/web/rss/default.nix @@ -2,31 +2,52 @@ let cfg = config.services.rss-bridge; - debug = true; - rss-bridge = pkgs.rss-bridge.overrideAttrs (oldAttrs: - oldAttrs // { - installPhase = oldAttrs.installPhase + '' - pushd $out/bridges - ln -sf ${./ParisJazzClubBridge.php} ParisJazzClubBridge.php - ln -sf ${./MaisonDeLaRadioBridge.php} MaisonDeLaRadioBridge.php - ln -sf ${./FipAlbumsBridge.php} FipAlbumsBridge.php - ln -sf ${./WhatsOnMubiBridge.php} WhatsOnMubiBridge.php - popd - '' + lib.optionalString debug '' - touch $out/DEBUG - ''; - }); + # debug = true; + # rss-bridge = pkgs.rss-bridge.overrideAttrs (oldAttrs: + # oldAttrs // { + # installPhase = oldAttrs.installPhase + '' + # pushd $out/bridges + # ln -sf ${./ParisJazzClubBridge.php} ParisJazzClubBridge.php + # ln -sf ${./MaisonDeLaRadioBridge.php} MaisonDeLaRadioBridge.php + # ln -sf ${./FipAlbumsBridge.php} FipAlbumsBridge.php + # ln -sf ${./WhatsOnMubiBridge.php} WhatsOnMubiBridge.php + # popd + # '' + lib.optionalString debug '' + # touch $out/DEBUG + # ''; + # }); in { services.rss-bridge = { enable = true; - whitelist = [ "ParisJazzClub" "MaisonDeLaRadio" "FipAlbumsBridge" "WhatsOnMubi" ]; + # whitelist = [ "ParisJazzClub" "MaisonDeLaRadio" "FipAlbumsBridge" "WhatsOnMubi" ]; + extraBridges = [ + # Music + { + name = "FipAlbums"; + source = ./FipAlbumsBridge.php; + } + ## Concerts + { + name = "ParisJazzClub"; + source = ./ParisJazzClubBridge.php; + } + { + name = "MaisonDeLaRadio"; + source = ./MaisonDeLaRadioBridge.php; + } + # Cinema + { + name = "WhatsOnMubi"; + source = ./WhatsOnMubiBridge.php; + } + ]; virtualHost = "rss"; }; services.nginx = lib.mkIf (cfg.virtualHost != null) { virtualHosts.${cfg.virtualHost} = { serverName = "rss.${config.networking.domain}"; - root = lib.mkForce "${rss-bridge}"; + # root = lib.mkForce "${rss-bridge}"; forceSSL = true; enableACME = true; }; diff --git a/modules/default.nix b/modules/default.nix index d09a8c0..95f75b4 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -3,5 +3,6 @@ { imports = [ ./filtron.nix + ./rss-bridge.nix ]; } diff --git a/modules/rss-bridge.nix b/modules/rss-bridge.nix new file mode 100644 index 0000000..c7aaf05 --- /dev/null +++ b/modules/rss-bridge.nix @@ -0,0 +1,71 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.rss-bridge; + rss-bridge = pkgs.rss-bridge.overrideAttrs (oldAttrs: + oldAttrs // { + installPhase = oldAttrs.installPhase + '' + pushd $out/bridges + ln -sf ${./ParisJazzClubBridge.php} ParisJazzClubBridge.php + ln -sf ${./MaisonDeLaRadioBridge.php} MaisonDeLaRadioBridge.php + ln -sf ${./FipAlbumsBridge.php} FipAlbumsBridge.php + ln -sf ${./WhatsOnMubiBridge.php} WhatsOnMubiBridge.php + popd + '' + lib.optionalString debug '' + touch $out/DEBUG + ''; + }); +in { + options.services.rss-bridge = { + package = mkOption { + type = types.package; + description = "Which derivation to use."; + default = pkgs.rss-bridge; + defaultText = literalExample "pkgs.rss-bridge"; + }; + debug = mkEnableOption "debug mode"; + extraBridges = mkOption { + type = types.listOf (types.submodule { + options = { + name = mkOption { + type = types.strMatching "[a-zA-Z0-9]*"; + description = '' + The name of the bridge. + It need not include 'Bridge' at the end, unlike required in RSS-Bridge. + ''; + example = "SomeAppWithANewsletter"; + }; + source = mkOption { + type = types.path; + description = '' + The path to a file whose contents is the PHP sourcecode of the bridge. + See also the RSS-Bridge documentation: https://rss-bridge.github.io/rss-bridge/Bridge_API/index.html. + ''; + }; + }; + }); + default = [ ]; + description = '' + A list of additional bridges that aren't already included in RSS-Bridge. + These bridges are automatically whitelisted''; + }; + }; + + config.services.rss-bridge.whitelist = + map (bridge: bridge.name) cfg.extraBridges; + config.services.nginx = mkIf (cfg.virtualHost != null) { + virtualHosts.${cfg.virtualHost}.root = mkIf (cfg.extraBridges != [ ]) + (mkForce (pkgs.runCommand "rss-bridge" { } ('' + mkdir -p $out/bridges + cp -r ${cfg.package}/* $out/ + pushd $out/bridges + '' + concatStringsSep "\n" + (map (bridge: ''ln -sf ${bridge.source} "${bridge.name}Bridge.php"'') + cfg.extraBridges) + '' + popd + '' + lib.optionalString cfg.debug '' + touch $out/DEBUG + ''))); + }; +} diff --git a/tests/configuration.nix b/tests/configuration.nix index 37b043c..a1ccf52 100644 --- a/tests/configuration.nix +++ b/tests/configuration.nix @@ -29,6 +29,8 @@ in { services.filtron.rules = lib.mkForce [ ]; + services.rss-bridge.debug = true; + services.nginx.virtualHosts = { quentin = nginxMakeLocal 8080; searx = nginxMakeLocal 8081; -- cgit v1.3.1 From 3362029ceb7076c9351ffac34133669faf661ac1 Mon Sep 17 00:00:00 2001 From: Quentin Aristote Date: Sun, 4 Dec 2022 13:11:10 +0100 Subject: services: web: searx: engines: update to v1.1.0 --- config/services/web/searx/searx/engines.nix | 24 +----------------------- tests/run-tests.sh | 1 - 2 files changed, 1 insertion(+), 24 deletions(-) (limited to 'tests') diff --git a/config/services/web/searx/searx/engines.nix b/config/services/web/searx/searx/engines.nix index 5e4609a..76f0ca8 100644 --- a/config/services/web/searx/searx/engines.nix +++ b/config/services/web/searx/searx/engines.nix @@ -54,7 +54,7 @@ in { # files "btdigg" # images - "ccengine" + "openverse" "bing images" "library of congress" "qwant images" @@ -96,28 +96,6 @@ in { results = "HTML"; }; } - { # Emojipedia - name = "emojipedia"; - engine = "xpath"; - search_url = makeSearchUrl { - baseUrl = "https://emojipedia.org/search/"; - queryKeyword = "q"; - }; - results_xpath = ''//ol[@class="search-results"]/li/h2''; - url_xpath = "./a/@href"; - title_xpath = "./a"; - content_xpath = "../p"; - shortcut = "emoji"; - disabled = true; - about = { - website = "https://emojipedia.org/"; - wikidata_id = "Q22908129"; - official_api_documentation = ""; - use_official_api = false; - require_api_key = false; - results = "HTML"; - }; - } { name = "nlab"; engine = "xpath"; diff --git a/tests/run-tests.sh b/tests/run-tests.sh index 92acdc8..7842dc6 100755 --- a/tests/run-tests.sh +++ b/tests/run-tests.sh @@ -42,7 +42,6 @@ echo echo Checking custom Searx engines : declare -A QUERIES QUERIES[alternativeto]=Searx -QUERIES[emojipedia]='Thinking%20Face' QUERIES[nlab]='Kan%20extension' QUERIES[wikipediafr]=Paris QUERIES[wikipediaen]=Paris -- cgit v1.3.1 From 9bde3c4624eb916bafcf9a18792edb42e3a25a17 Mon Sep 17 00:00:00 2001 From: Quentin Aristote Date: Tue, 20 Dec 2022 22:29:23 +0100 Subject: searx: engines: wikipedia: fix xpath and test --- config/services/web/searx/searx/engines.nix | 6 +++--- tests/run-tests.sh | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/config/services/web/searx/searx/engines.nix b/config/services/web/searx/searx/engines.nix index 76f0ca8..5256c27 100644 --- a/config/services/web/searx/searx/engines.nix +++ b/config/services/web/searx/searx/engines.nix @@ -29,9 +29,9 @@ let extraParameters = { fulltext = 1; }; }; results_xpath = ''//ul[@class="mw-search-results"]/li''; - url_xpath = ''./div[@class="mw-search-result-heading"]/a/@href''; - title_xpath = ''./div[@class="mw-search-result-heading"]/a''; - content_xpath = ''./div[@class="searchresult"]''; + url_xpath = ''.//div[@class="mw-search-result-heading"]/a/@href''; + title_xpath = ''.//div[@class="mw-search-result-heading"]/a/@title''; + content_xpath = ''.//div[@class="searchresult"]''; shortcut = "w${lang}"; categories = "general"; disabled = true; diff --git a/tests/run-tests.sh b/tests/run-tests.sh index 7842dc6..705f28d 100755 --- a/tests/run-tests.sh +++ b/tests/run-tests.sh @@ -43,8 +43,8 @@ echo Checking custom Searx engines : declare -A QUERIES QUERIES[alternativeto]=Searx QUERIES[nlab]='Kan%20extension' -QUERIES[wikipediafr]=Paris -QUERIES[wikipediaen]=Paris +QUERIES[wfr]=Paris +QUERIES[wen]=Paris for ENGINE in "${!QUERIES[@]}" do echo Checking engine $ENGINE ... -- cgit v1.3.1