From 30cae53dbb1de9c3519da72fb36a1288da321b5b Mon Sep 17 00:00:00 2001
From: talyz
Date: Fri, 12 Mar 2021 12:27:21 +0100
Subject: [PATCH 1/9] v8: 7.4.255 -> 8.4.255
---
pkgs/development/libraries/v8/default.nix | 34 ++++++++++-----------
pkgs/development/libraries/v8/gcc_arm.patch | 31 +++++++++++++++++++
pkgs/top-level/all-packages.nix | 4 ---
pkgs/top-level/php-packages.nix | 8 ++---
4 files changed, 52 insertions(+), 25 deletions(-)
create mode 100644 pkgs/development/libraries/v8/gcc_arm.patch
diff --git a/pkgs/development/libraries/v8/default.nix b/pkgs/development/libraries/v8/default.nix
index c79073f04e4c..d513e01b7a35 100644
--- a/pkgs/development/libraries/v8/default.nix
+++ b/pkgs/development/libraries/v8/default.nix
@@ -11,23 +11,23 @@ let
deps = {
"base/trace_event/common" = fetchgit {
url = "${git_url}/chromium/src/base/trace_event/common.git";
- rev = "936ba8a963284a6b3737cf2f0474a7131073abee";
- sha256 = "14nr22fqdpxma1kzjflj6a865vr3hfnnm2gs4vcixyq4kmfzfcy2";
+ rev = "dab187b372fc17e51f5b9fad8201813d0aed5129";
+ sha256 = "0dmpj9hj4xv3xb0fl1kb9hm4bhpbs2s5csx3z8cgjd5vwvhdzig4";
};
build = fetchgit {
url = "${git_url}/chromium/src/build.git";
- rev = "325e95d6dae64f35b160b3dc7d73218cee5ec079";
- sha256 = "0dddyxa76p2xpjhmxif05v63i5ar6h5v684fdl667sg84f5bhhxf";
+ rev = "26e9d485d01d6e0eb9dadd21df767a63494c8fea";
+ sha256 = "1jjvsgj0cs97d26i3ba531ic1f9gqan8x7z4aya8yl8jx02l342q";
};
"third_party/googletest/src" = fetchgit {
url = "${git_url}/external/github.com/google/googletest.git";
- rev = "5ec7f0c4a113e2f18ac2c6cc7df51ad6afc24081";
- sha256 = "0gmr10042c0xybxnn6g7ndj1na1mmd3l9w7449qlcv4s8gmfs7k6";
+ rev = "e3f0319d89f4cbf32993de595d984183b1a9fc57";
+ sha256 = "18xz71l2xjrqsc0q317whgw4xi1i5db24zcj7v04f5g6r1hyf1a5";
};
"third_party/icu" = fetchgit {
url = "${git_url}/chromium/deps/icu.git";
- rev = "960f195aa87acaec46e6104ec93a596da7ae0843";
- sha256 = "073kh6gpcairgjxf3hlhpqljc13gwl2aj8fz91fv220xibwqs834";
+ rev = "f2223961702f00a8833874b0560d615a2cc42738";
+ sha256 = "0z5p53kbrjfkjn0i12dpk55cp8976j2zk7a4wk88423s2c5w87zl";
};
"third_party/jinja2" = fetchgit {
url = "${git_url}/chromium/src/third_party/jinja2.git";
@@ -39,29 +39,31 @@ let
rev = "8f45f5cfa0009d2a70589bcda0349b8cb2b72783";
sha256 = "168ppjmicfdh4i1l0l25s86mdbrz9fgxmiq1rx33x79mph41scfz";
};
+ "third_party/zlib" = fetchgit {
+ url = "${git_url}/chromium/src/third_party/zlib.git";
+ rev = "156be8c52f80cde343088b4a69a80579101b6e67";
+ sha256 = "0hxbkkzmlv714fjq2jlp5dd2jc339xyh6gkjx1sz3srwv33mlk92";
+ };
};
in
stdenv.mkDerivation rec {
pname = "v8";
- version = "7.4.255";
+ version = "8.4.255";
doCheck = true;
patches = [
- (fetchpatch {
- url = "https://raw.githubusercontent.com/RPi-Distro/chromium-browser/master/debian/patches/revert-Xclang-instcombine-lower-dbg-declare.patch";
- sha256 = "02hczcg43m36q8j1kv5j3hq9czj9niiil9w13w22vzv2f3c67dvn";
- })
./darwin.patch
+ ./gcc_arm.patch # Fix building zlib with gcc on aarch64, from https://gist.github.com/Adenilson/d973b6fd96c7709d33ddf08cf1dcb149
];
src = fetchFromGitHub {
owner = "v8";
repo = "v8";
rev = version;
- sha256 = "14i0c71hmffzqnq9n73dh9dnabdxhbjhzkhqpk5yv9y90bwrzi2n";
+ sha256 = "07ymw4kqbz7kv311gpk5bs5q90wj73n2q7jkyfhqk4hvhs1q5bw7";
};
postUnpack = ''
@@ -97,9 +99,7 @@ stdenv.mkDerivation rec {
''v8_snapshot_toolchain="//build/toolchain/linux/unbundle:default"''
] ++ lib.optional stdenv.cc.isClang ''clang_base_path="${stdenv.cc}"'';
- # with gcc8, -Wclass-memaccess became part of -Wall and causes logging limit
- # to be exceeded
- NIX_CFLAGS_COMPILE = lib.optionalString stdenv.cc.isGNU "-Wno-class-memaccess";
+ NIX_CFLAGS_COMPILE = "-O2";
nativeBuildInputs = [ gn ninja pkg-config python ]
++ lib.optionals stdenv.isDarwin [ xcbuild darwin.DarwinTools ];
diff --git a/pkgs/development/libraries/v8/gcc_arm.patch b/pkgs/development/libraries/v8/gcc_arm.patch
new file mode 100644
index 000000000000..09579561fd8d
--- /dev/null
+++ b/pkgs/development/libraries/v8/gcc_arm.patch
@@ -0,0 +1,31 @@
+diff --git a/third_party/zlib/contrib/optimizations/insert_string.h b/third_party/zlib/contrib/optimizations/insert_string.h
+index 1826601..d123305 100644
+--- a/third_party/zlib/contrib/optimizations/insert_string.h
++++ b/third_party/zlib/contrib/optimizations/insert_string.h
+@@ -26,15 +26,23 @@
+ #define _cpu_crc32_u32 _mm_crc32_u32
+
+ #elif defined(CRC32_ARMV8_CRC32)
+- #if defined(__clang__)
++ #if defined(__GNUC__) || defined(__clang__)
+ #undef TARGET_CPU_WITH_CRC
+- #define __crc32cw __builtin_arm_crc32cw
++ #if defined(__clang__)
++ #define __crc32cw __builtin_arm_crc32cw
++ #elif defined(__GNUC__)
++ #define __crc32cw __builtin_aarch64_crc32cw
++ #endif
+ #endif
+
+ #define _cpu_crc32_u32 __crc32cw
+
+ #if defined(__aarch64__)
+- #define TARGET_CPU_WITH_CRC __attribute__((target("crc")))
++ #if defined(__clang__)
++ #define TARGET_CPU_WITH_CRC __attribute__((target("crc")))
++ #elif defined(__GNUC__)
++ #define TARGET_CPU_WITH_CRC __attribute__((target("+crc")))
++ #endif
+ #else // !defined(__aarch64__)
+ #define TARGET_CPU_WITH_CRC __attribute__((target("armv8-a,crc")))
+ #endif // defined(__aarch64__)
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index 7948c0d0fea2..490fac7cfe36 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -17492,12 +17492,8 @@ in
stdenv = gcc6Stdenv;
});
- v8_6_x = v8;
v8 = callPackage ../development/libraries/v8 {
inherit (python2Packages) python;
- } // lib.optionalAttrs stdenv.isLinux {
- # doesn't build with gcc7
- stdenv = gcc6Stdenv;
};
vaapiIntel = callPackage ../development/libraries/vaapi-intel { };
diff --git a/pkgs/top-level/php-packages.nix b/pkgs/top-level/php-packages.nix
index 7db0eb4d6a4c..3bbcb453a3c8 100644
--- a/pkgs/top-level/php-packages.nix
+++ b/pkgs/top-level/php-packages.nix
@@ -146,8 +146,8 @@ lib.makeScope pkgs.newScope (self: with self; {
sha256 = "103nys7zkpi1hifqp9miyl0m1mn07xqshw3sapyz365nb35g5q71";
- buildInputs = [ pkgs.v8_6_x ];
- configureFlags = [ "--with-v8=${pkgs.v8_6_x}" ];
+ buildInputs = [ pkgs.v8 ];
+ configureFlags = [ "--with-v8=${pkgs.v8}" ];
meta.maintainers = lib.teams.php.members;
meta.broken = true;
@@ -159,8 +159,8 @@ lib.makeScope pkgs.newScope (self: with self; {
sha256 = "0g63dyhhicngbgqg34wl91nm3556vzdgkq19gy52gvmqj47rj6rg";
- buildInputs = [ pkgs.v8_6_x ];
- configureFlags = [ "--with-v8js=${pkgs.v8_6_x}" ];
+ buildInputs = [ pkgs.v8 ];
+ configureFlags = [ "--with-v8js=${pkgs.v8}" ];
meta.maintainers = lib.teams.php.members;
meta.broken = true;
From 46d935a4ce9f509ccb4792eca0261e872dd16e20 Mon Sep 17 00:00:00 2001
From: talyz
Date: Sat, 3 Apr 2021 14:13:48 +0200
Subject: [PATCH 2/9] nixos/nginx: Add an option to specify additional
third-party modules
---
.../modules/services/web-servers/nginx/default.nix | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 6c8341c1115f..750677f6165d 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -397,6 +397,9 @@ in
default = pkgs.nginxStable;
defaultText = "pkgs.nginxStable";
type = types.package;
+ apply = p: p.override {
+ modules = p.modules ++ cfg.additionalModules;
+ };
description = "
Nginx package to use. This defaults to the stable version. Note
that the nginx team recommends to use the mainline version which
@@ -404,6 +407,17 @@ in
";
};
+ additionalModules = mkOption {
+ default = [];
+ type = types.listOf (types.attrsOf types.anything);
+ example = literalExample "[ pkgs.nginxModules.brotli ]";
+ description = ''
+ Additional third-party nginx modules
+ to install. Packaged modules are available in
+ pkgs.nginxModules .
+ '';
+ };
+
logError = mkOption {
default = "stderr";
type = types.str;
From 4d8c8f472215961e617de579603079109908bebc Mon Sep 17 00:00:00 2001
From: talyz
Date: Fri, 12 Mar 2021 12:58:31 +0100
Subject: [PATCH 3/9] discourse: init at 2.6.3
---
.../discourse/action_mailer_ca_cert.patch | 12 +
.../web-apps/discourse/admin_create.patch | 48 +
pkgs/servers/web-apps/discourse/default.nix | 233 ++
.../web-apps/discourse/disable_jhead.patch | 12 +
.../web-apps/discourse/nixos_defaults.patch | 13 +
.../web-apps/discourse/rubyEnv/Gemfile | 248 ++
.../web-apps/discourse/rubyEnv/Gemfile.lock | 561 ++++
.../web-apps/discourse/rubyEnv/gemset.nix | 2272 +++++++++++++++++
.../unicorn_logging_and_timeout.patch | 25 +
pkgs/top-level/all-packages.nix | 4 +
10 files changed, 3428 insertions(+)
create mode 100644 pkgs/servers/web-apps/discourse/action_mailer_ca_cert.patch
create mode 100644 pkgs/servers/web-apps/discourse/admin_create.patch
create mode 100644 pkgs/servers/web-apps/discourse/default.nix
create mode 100644 pkgs/servers/web-apps/discourse/disable_jhead.patch
create mode 100644 pkgs/servers/web-apps/discourse/nixos_defaults.patch
create mode 100644 pkgs/servers/web-apps/discourse/rubyEnv/Gemfile
create mode 100644 pkgs/servers/web-apps/discourse/rubyEnv/Gemfile.lock
create mode 100644 pkgs/servers/web-apps/discourse/rubyEnv/gemset.nix
create mode 100644 pkgs/servers/web-apps/discourse/unicorn_logging_and_timeout.patch
diff --git a/pkgs/servers/web-apps/discourse/action_mailer_ca_cert.patch b/pkgs/servers/web-apps/discourse/action_mailer_ca_cert.patch
new file mode 100644
index 000000000000..83c44a466fac
--- /dev/null
+++ b/pkgs/servers/web-apps/discourse/action_mailer_ca_cert.patch
@@ -0,0 +1,12 @@
+diff --git a/config/environments/production.rb b/config/environments/production.rb
+index 75c3a69512..7fc374cd9d 100644
+--- a/config/environments/production.rb
++++ b/config/environments/production.rb
+@@ -32,6 +32,7 @@ Discourse::Application.configure do
+ user_name: GlobalSetting.smtp_user_name,
+ password: GlobalSetting.smtp_password,
+ authentication: GlobalSetting.smtp_authentication,
++ ca_file: "/etc/ssl/certs/ca-certificates.crt",
+ enable_starttls_auto: GlobalSetting.smtp_enable_start_tls
+ }
+
diff --git a/pkgs/servers/web-apps/discourse/admin_create.patch b/pkgs/servers/web-apps/discourse/admin_create.patch
new file mode 100644
index 000000000000..651e8ce81dc9
--- /dev/null
+++ b/pkgs/servers/web-apps/discourse/admin_create.patch
@@ -0,0 +1,48 @@
+diff --git a/lib/tasks/admin.rake b/lib/tasks/admin.rake
+index 80c403616d..cba01202ac 100644
+--- a/lib/tasks/admin.rake
++++ b/lib/tasks/admin.rake
+@@ -107,3 +107,43 @@ task "admin:create" => :environment do
+ end
+
+ end
++
++desc "Creates a forum administrator noninteractively"
++task "admin:create_noninteractively" => :environment do
++ email = ENV["ADMIN_EMAIL"]
++ existing_user = User.find_by_email(email)
++
++ # check if user account already exixts
++ if existing_user
++ admin = existing_user
++ else
++ # create new user
++ admin = User.new
++ end
++
++ admin.email = email
++ admin.name = ENV["ADMIN_NAME"]
++ admin.username = ENV["ADMIN_USERNAME"]
++
++ password = ENV["ADMIN_PASSWORD"]
++ unless admin.confirm_password?(password)
++ admin.password = password
++ puts "Admin password set!"
++ end
++
++ admin.active = true
++
++ # save/update user account
++ saved = admin.save
++ raise admin.errors.full_messages.join("\n") unless saved
++
++ puts "Account created successfully with username #{admin.username}" unless existing_user
++
++ # grant admin privileges
++ admin.grant_admin!
++ if admin.trust_level < 1
++ admin.change_trust_level!(1)
++ end
++ admin.email_tokens.update_all confirmed: true
++ admin.activate
++end
diff --git a/pkgs/servers/web-apps/discourse/default.nix b/pkgs/servers/web-apps/discourse/default.nix
new file mode 100644
index 000000000000..8b1a2941eea6
--- /dev/null
+++ b/pkgs/servers/web-apps/discourse/default.nix
@@ -0,0 +1,233 @@
+{ stdenv, makeWrapper, runCommandNoCC, lib
+, fetchFromGitHub, bundlerEnv, ruby, replace, gzip, gnutar, git
+, util-linux, gawk, imagemagick, optipng, pngquant, libjpeg, jpegoptim
+, gifsicle, libpsl, redis, postgresql, which, brotli, procps
+, nodePackages, v8
+}:
+
+let
+ version = "2.6.3";
+
+ src = fetchFromGitHub {
+ owner = "discourse";
+ repo = "discourse";
+ rev = "v${version}";
+ sha256 = "sha256-lAIhVxvmjxEiru1KNxbFV+eDMLUGza/Dma3WU0ex0xs=";
+ };
+
+ runtimeDeps = [
+ # For backups, themes and assets
+ rubyEnv.wrappedRuby
+ gzip
+ gnutar
+ git
+ brotli
+
+ # Misc required system utils
+ which
+ procps # For ps and kill
+ util-linux # For renice
+ gawk
+
+ # Image optimization
+ imagemagick
+ optipng
+ pngquant
+ libjpeg
+ jpegoptim
+ gifsicle
+ nodePackages.svgo
+ ];
+
+ runtimeEnv = {
+ HOME = "/run/discourse/home";
+ RAILS_ENV = "production";
+ UNICORN_LISTENER = "/run/discourse/sockets/unicorn.sock";
+ };
+
+ rake = runCommandNoCC "discourse-rake" {
+ nativeBuildInputs = [ makeWrapper ];
+ } ''
+ mkdir -p $out/bin
+ makeWrapper ${rubyEnv}/bin/rake $out/bin/discourse-rake \
+ ${lib.concatStrings (lib.mapAttrsToList (name: value: "--set ${name} '${value}' ") runtimeEnv)} \
+ --prefix PATH : ${lib.makeBinPath runtimeDeps} \
+ --set RAKEOPT '-f ${discourse}/share/discourse/Rakefile' \
+ --run 'cd ${discourse}/share/discourse'
+ '';
+
+ rubyEnv = bundlerEnv {
+ name = "discourse-ruby-env-${version}";
+ inherit version ruby;
+ gemdir = ./rubyEnv;
+ gemset =
+ let
+ gems = import ./rubyEnv/gemset.nix;
+ in
+ gems // {
+ mini_racer = gems.mini_racer // {
+ buildInputs = [ v8 ];
+ dontBuild = false;
+ # The Ruby extension makefile generator assumes the source
+ # is C, when it's actually C++ ¯\_(ツ)_/¯
+ postPatch = ''
+ substituteInPlace ext/mini_racer_extension/extconf.rb \
+ --replace '" -std=c++0x"' \
+ '" -x c++ -std=c++0x"'
+ '';
+ };
+ mini_suffix = gems.mini_suffix // {
+ propagatedBuildInputs = [ libpsl ];
+ dontBuild = false;
+ # Use our libpsl instead of the vendored one, which isn't
+ # available for aarch64
+ postPatch = ''
+ cp $(readlink -f ${libpsl}/lib/libpsl.so) vendor/libpsl.so
+ '';
+ };
+ };
+
+ groups = [
+ "default" "assets" "development" "test"
+ ];
+ };
+
+ assets = stdenv.mkDerivation {
+ pname = "discourse-assets";
+ inherit version src;
+
+ nativeBuildInputs = [
+ rubyEnv.wrappedRuby
+ postgresql
+ redis
+ which
+ brotli
+ procps
+ nodePackages.uglify-js
+ ];
+
+ # We have to set up an environment that is close enough to
+ # production ready or the assets:precompile task refuses to
+ # run. This means that Redis and PostgreSQL has to be running and
+ # database migrations performed.
+ preBuild = ''
+ redis-server >/dev/null &
+
+ initdb -A trust $NIX_BUILD_TOP/postgres >/dev/null
+ postgres -D $NIX_BUILD_TOP/postgres -k $NIX_BUILD_TOP >/dev/null &
+ export PGHOST=$NIX_BUILD_TOP
+
+ echo "Waiting for Redis and PostgreSQL to be ready.."
+ while ! redis-cli --scan >/dev/null || ! psql -l >/dev/null; do
+ sleep 0.1
+ done
+
+ psql -d postgres -tAc 'CREATE USER "discourse"'
+ psql -d postgres -tAc 'CREATE DATABASE "discourse" OWNER "discourse"'
+ psql 'discourse' -tAc "CREATE EXTENSION IF NOT EXISTS pg_trgm"
+ psql 'discourse' -tAc "CREATE EXTENSION IF NOT EXISTS hstore"
+
+ # Create a temporary home dir to stop bundler from complaining
+ mkdir $NIX_BUILD_TOP/tmp_home
+ export HOME=$NIX_BUILD_TOP/tmp_home
+
+ export RAILS_ENV=production
+
+ bundle exec rake db:migrate >/dev/null
+ rm -r tmp/*
+ '';
+
+ buildPhase = ''
+ runHook preBuild
+
+ bundle exec rake assets:precompile
+
+ runHook postBuild
+ '';
+
+ installPhase = ''
+ runHook preInstall
+
+ mv public/assets $out
+
+ runHook postInstall
+ '';
+ };
+
+ discourse = stdenv.mkDerivation {
+ pname = "discourse";
+ inherit version src;
+
+ buildInputs = [
+ rubyEnv rubyEnv.wrappedRuby rubyEnv.bundler
+ ];
+
+ patches = [
+ # Load a separate NixOS site settings file
+ ./nixos_defaults.patch
+
+ # Add a noninteractive admin creation task
+ ./admin_create.patch
+
+ # Disable jhead, which is currently marked as vulnerable
+ ./disable_jhead.patch
+
+ # Add the path to the CA cert bundle to make TLS work
+ ./action_mailer_ca_cert.patch
+
+ # Log Unicorn messages to the journal and make request timeout
+ # configurable
+ ./unicorn_logging_and_timeout.patch
+ ];
+
+ postPatch = ''
+ # Always require lib-files and application.rb through their store
+ # path, not their relative state directory path. This gets rid of
+ # warnings and means we don't have to link back to lib from the
+ # state directory.
+ find config -type f -execdir sed -Ei "s,(\.\./)+(lib|app)/,$out/share/discourse/\2/," {} \;
+
+ ${replace}/bin/replace-literal -f -r -e 'File.rename(temp_destination, destination)' "FileUtils.mv(temp_destination, destination)" .
+ '';
+
+ buildPhase = ''
+ runHook preBuild
+
+ mv config config.dist
+ mv public public.dist
+ mv plugins plugins.dist
+
+ runHook postBuild
+ '';
+
+ installPhase = ''
+ runHook preInstall
+
+ mkdir -p $out/share
+ cp -r . $out/share/discourse
+ rm -r $out/share/discourse/log
+ ln -sf /var/log/discourse $out/share/discourse/log
+ ln -sf /run/discourse/tmp $out/share/discourse/tmp
+ ln -sf /run/discourse/config $out/share/discourse/config
+ ln -sf /run/discourse/assets/javascripts/plugins $out/share/discourse/app/assets/javascripts/plugins
+ ln -sf /run/discourse/public $out/share/discourse/public
+ ln -sf /run/discourse/plugins $out/share/discourse/plugins
+ ln -sf ${assets} $out/share/discourse/public.dist/assets
+
+ runHook postInstall
+ '';
+
+ meta = with lib; {
+ homepage = "https://www.discourse.org/";
+ platforms = platforms.linux;
+ maintainers = with maintainers; [ talyz ];
+ license = licenses.gpl2Plus;
+ description = "Discourse is an open source discussion platform";
+ };
+
+ passthru = {
+ inherit rubyEnv runtimeEnv runtimeDeps rake;
+ ruby = rubyEnv.wrappedRuby;
+ };
+ };
+in discourse
diff --git a/pkgs/servers/web-apps/discourse/disable_jhead.patch b/pkgs/servers/web-apps/discourse/disable_jhead.patch
new file mode 100644
index 000000000000..709a1959d637
--- /dev/null
+++ b/pkgs/servers/web-apps/discourse/disable_jhead.patch
@@ -0,0 +1,12 @@
+diff --git a/lib/file_helper.rb b/lib/file_helper.rb
+index 162de9a40b..9ac8807e9d 100644
+--- a/lib/file_helper.rb
++++ b/lib/file_helper.rb
+@@ -124,6 +124,7 @@ class FileHelper
+ jpegoptim: { strip: strip_image_metadata ? "all" : "none" },
+ jpegtran: false,
+ jpegrecompress: false,
++ jhead: false,
+ )
+ end
+ end
diff --git a/pkgs/servers/web-apps/discourse/nixos_defaults.patch b/pkgs/servers/web-apps/discourse/nixos_defaults.patch
new file mode 100644
index 000000000000..3efca97e62c0
--- /dev/null
+++ b/pkgs/servers/web-apps/discourse/nixos_defaults.patch
@@ -0,0 +1,13 @@
+diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb
+index 89a5e923fc..b60754f50a 100644
+--- a/app/models/site_setting.rb
++++ b/app/models/site_setting.rb
+@@ -26,6 +26,8 @@ class SiteSetting < ActiveRecord::Base
+ end
+ end
+
++ load_settings(File.join(Rails.root, 'config', 'nixos_site_settings.json'))
++
+ setup_deprecated_methods
+ client_settings << :available_locales
+
diff --git a/pkgs/servers/web-apps/discourse/rubyEnv/Gemfile b/pkgs/servers/web-apps/discourse/rubyEnv/Gemfile
new file mode 100644
index 000000000000..cb86d7e4bacf
--- /dev/null
+++ b/pkgs/servers/web-apps/discourse/rubyEnv/Gemfile
@@ -0,0 +1,248 @@
+# frozen_string_literal: true
+
+source 'https://rubygems.org'
+# if there is a super emergency and rubygems is playing up, try
+#source 'http://production.cf.rubygems.org'
+
+gem 'bootsnap', require: false, platform: :mri
+
+def rails_master?
+ ENV["RAILS_MASTER"] == '1'
+end
+
+if rails_master?
+ gem 'arel', git: 'https://github.com/rails/arel.git'
+ gem 'rails', git: 'https://github.com/rails/rails.git'
+else
+ # NOTE: Until rubygems gives us optional dependencies we are stuck with this needing to be explicit
+ # this allows us to include the bits of rails we use without pieces we do not.
+ #
+ # To issue a rails update bump the version number here
+ gem 'actionmailer', '6.0.3.3'
+ gem 'actionpack', '6.0.3.3'
+ gem 'actionview', '6.0.3.3'
+ gem 'activemodel', '6.0.3.3'
+ gem 'activerecord', '6.0.3.3'
+ gem 'activesupport', '6.0.3.3'
+ gem 'railties', '6.0.3.3'
+ gem 'sprockets-rails'
+end
+
+gem 'json'
+
+# TODO: At the moment Discourse does not work with Sprockets 4, we would need to correct internals
+# This is a desired upgrade we should get to.
+gem 'sprockets', '3.7.2'
+
+# this will eventually be added to rails,
+# allows us to precompile all our templates in the unicorn master
+gem 'actionview_precompiler', require: false
+
+gem 'seed-fu'
+
+gem 'mail', require: false
+gem 'mini_mime'
+gem 'mini_suffix'
+
+gem 'redis'
+
+# This is explicitly used by Sidekiq and is an optional dependency.
+# We tell Sidekiq to use the namespace "sidekiq" which triggers this
+# gem to be used. There is no explicit dependency in sidekiq cause
+# redis namespace support is optional
+# We already namespace stuff in DiscourseRedis, so we should consider
+# just using a single implementation in core vs having 2 namespace implementations
+gem 'redis-namespace'
+
+# NOTE: AM serializer gets a lot slower with recent updates
+# we used an old branch which is the fastest one out there
+# are long term goal here is to fork this gem so we have a
+# better maintained living fork
+gem 'active_model_serializers', '~> 0.8.3'
+
+gem 'onebox'
+
+gem 'http_accept_language', require: false
+
+# Ember related gems need to be pinned cause they control client side
+# behavior, we will push these versions up when upgrading ember
+gem 'discourse-ember-rails', '0.18.6', require: 'ember-rails'
+gem 'discourse-ember-source', '~> 3.12.2'
+gem 'ember-handlebars-template', '0.8.0'
+gem 'discourse-fonts'
+
+gem 'barber'
+
+gem 'message_bus'
+
+gem 'rails_multisite'
+
+gem 'fast_xs', platform: :ruby
+
+gem 'xorcist'
+
+gem 'fastimage'
+
+gem 'aws-sdk-s3', require: false
+gem 'aws-sdk-sns', require: false
+gem 'excon', require: false
+gem 'unf', require: false
+
+gem 'email_reply_trimmer'
+
+# Forked until https://github.com/toy/image_optim/pull/162 is merged
+# https://github.com/discourse/image_optim
+gem 'discourse_image_optim', require: 'image_optim'
+gem 'multi_json'
+gem 'mustache'
+gem 'nokogiri'
+gem 'css_parser', require: false
+
+gem 'omniauth'
+gem 'omniauth-facebook'
+gem 'omniauth-twitter'
+gem 'omniauth-github'
+
+gem 'omniauth-oauth2', require: false
+
+gem 'omniauth-google-oauth2'
+
+gem 'oj'
+gem 'pg'
+gem 'mini_sql'
+gem 'pry-rails', require: false
+gem 'pry-byebug', require: false
+gem 'r2', require: false
+gem 'rake'
+
+gem 'thor', require: false
+gem 'diffy', require: false
+gem 'rinku'
+gem 'sidekiq'
+gem 'mini_scheduler'
+
+gem 'execjs', require: false
+gem 'mini_racer'
+
+gem 'highline', require: false
+
+gem 'rack'
+
+gem 'rack-protection' # security
+gem 'cbor', require: false
+gem 'cose', require: false
+gem 'addressable'
+
+# Gems used only for assets and not required in production environments by default.
+# Allow everywhere for now cause we are allowing asset debugging in production
+group :assets do
+ gem 'uglifier'
+ gem 'rtlit', require: false # for css rtling
+end
+
+group :test do
+ gem 'webmock', require: false
+ gem 'fakeweb', require: false
+ gem 'minitest', require: false
+ gem 'simplecov', require: false
+ gem "test-prof"
+end
+
+group :test, :development do
+ gem 'rspec'
+ gem 'mock_redis'
+ gem 'listen', require: false
+ gem 'certified', require: false
+ gem 'fabrication', require: false
+ gem 'mocha', require: false
+
+ gem 'rb-fsevent', require: RUBY_PLATFORM =~ /darwin/i ? 'rb-fsevent' : false
+
+ gem 'rspec-rails'
+
+ gem 'shoulda-matchers', require: false
+ gem 'rspec-html-matchers'
+ gem 'byebug', require: ENV['RM_INFO'].nil?, platform: :mri
+ gem "rubocop-discourse", require: false
+ gem 'parallel_tests'
+
+ gem 'rswag-specs'
+end
+
+group :development do
+ gem 'ruby-prof', require: false, platform: :mri
+ gem 'bullet', require: !!ENV['BULLET']
+ gem 'better_errors', platform: :mri, require: !!ENV['BETTER_ERRORS']
+ gem 'binding_of_caller'
+ gem 'yaml-lint'
+ gem 'annotate'
+end
+
+# this is an optional gem, it provides a high performance replacement
+# to String#blank? a method that is called quite frequently in current
+# ActiveRecord, this may change in the future
+gem 'fast_blank', platform: :ruby
+
+# this provides a very efficient lru cache
+gem 'lru_redux'
+
+gem 'htmlentities', require: false
+
+# IMPORTANT: mini profiler monkey patches, so it better be required last
+# If you want to amend mini profiler to do the monkey patches in the railties
+# we are open to it. by deferring require to the initializer we can configure discourse installs without it
+
+gem 'flamegraph', require: false
+gem 'rack-mini-profiler', require: ['enable_rails_patches']
+
+gem 'unicorn', require: false, platform: :ruby
+gem 'puma', require: false
+gem 'rbtrace', require: false, platform: :mri
+gem 'gc_tracer', require: false, platform: :mri
+
+# required for feed importing and embedding
+gem 'ruby-readability', require: false
+
+gem 'stackprof', require: false, platform: :mri
+gem 'memory_profiler', require: false, platform: :mri
+
+gem 'cppjieba_rb', require: false
+
+gem 'lograge', require: false
+gem 'logstash-event', require: false
+gem 'logstash-logger', require: false
+gem 'logster'
+
+# NOTE: later versions of sassc are causing a segfault, possibly dependent on processer architecture
+# and until resolved should be locked at 2.0.1
+gem 'sassc', '2.0.1', require: false
+gem "sassc-rails"
+
+gem 'rotp', require: false
+
+gem 'rqrcode'
+
+gem 'rubyzip', require: false
+
+gem 'sshkey', require: false
+
+gem 'rchardet', require: false
+gem 'lz4-ruby', require: false, platform: :ruby
+
+if ENV["IMPORT"] == "1"
+ gem 'mysql2'
+ gem 'redcarpet'
+
+ # NOTE: in import mode the version of sqlite can matter a lot, so we stick it to a specific one
+ gem 'sqlite3', '~> 1.3', '>= 1.3.13'
+ gem 'ruby-bbcode-to-md', git: 'https://github.com/nlalonde/ruby-bbcode-to-md'
+ gem 'reverse_markdown'
+ gem 'tiny_tds'
+ gem 'csv'
+end
+
+gem 'webpush', require: false
+gem 'colored2', require: false
+gem 'maxminddb'
+
+gem 'rails_failover', require: false
diff --git a/pkgs/servers/web-apps/discourse/rubyEnv/Gemfile.lock b/pkgs/servers/web-apps/discourse/rubyEnv/Gemfile.lock
new file mode 100644
index 000000000000..4f0674932277
--- /dev/null
+++ b/pkgs/servers/web-apps/discourse/rubyEnv/Gemfile.lock
@@ -0,0 +1,561 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ actionmailer (6.0.3.3)
+ actionpack (= 6.0.3.3)
+ actionview (= 6.0.3.3)
+ activejob (= 6.0.3.3)
+ mail (~> 2.5, >= 2.5.4)
+ rails-dom-testing (~> 2.0)
+ actionpack (6.0.3.3)
+ actionview (= 6.0.3.3)
+ activesupport (= 6.0.3.3)
+ rack (~> 2.0, >= 2.0.8)
+ rack-test (>= 0.6.3)
+ rails-dom-testing (~> 2.0)
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
+ actionview (6.0.3.3)
+ activesupport (= 6.0.3.3)
+ builder (~> 3.1)
+ erubi (~> 1.4)
+ rails-dom-testing (~> 2.0)
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
+ actionview_precompiler (0.2.3)
+ actionview (>= 6.0.a)
+ active_model_serializers (0.8.4)
+ activemodel (>= 3.0)
+ activejob (6.0.3.3)
+ activesupport (= 6.0.3.3)
+ globalid (>= 0.3.6)
+ activemodel (6.0.3.3)
+ activesupport (= 6.0.3.3)
+ activerecord (6.0.3.3)
+ activemodel (= 6.0.3.3)
+ activesupport (= 6.0.3.3)
+ activesupport (6.0.3.3)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (>= 0.7, < 2)
+ minitest (~> 5.1)
+ tzinfo (~> 1.1)
+ zeitwerk (~> 2.2, >= 2.2.2)
+ addressable (2.7.0)
+ public_suffix (>= 2.0.2, < 5.0)
+ annotate (3.1.1)
+ activerecord (>= 3.2, < 7.0)
+ rake (>= 10.4, < 14.0)
+ ast (2.4.1)
+ aws-eventstream (1.1.0)
+ aws-partitions (1.390.0)
+ aws-sdk-core (3.109.2)
+ aws-eventstream (~> 1, >= 1.0.2)
+ aws-partitions (~> 1, >= 1.239.0)
+ aws-sigv4 (~> 1.1)
+ jmespath (~> 1.0)
+ aws-sdk-kms (1.39.0)
+ aws-sdk-core (~> 3, >= 3.109.0)
+ aws-sigv4 (~> 1.1)
+ aws-sdk-s3 (1.83.2)
+ aws-sdk-core (~> 3, >= 3.109.0)
+ aws-sdk-kms (~> 1)
+ aws-sigv4 (~> 1.1)
+ aws-sdk-sns (1.35.0)
+ aws-sdk-core (~> 3, >= 3.109.0)
+ aws-sigv4 (~> 1.1)
+ aws-sigv4 (1.2.2)
+ aws-eventstream (~> 1, >= 1.0.2)
+ barber (0.12.2)
+ ember-source (>= 1.0, < 3.1)
+ execjs (>= 1.2, < 3)
+ better_errors (2.9.1)
+ coderay (>= 1.0.0)
+ erubi (>= 1.0.0)
+ rack (>= 0.9.0)
+ binding_of_caller (0.8.0)
+ debug_inspector (>= 0.0.1)
+ bootsnap (1.5.1)
+ msgpack (~> 1.0)
+ builder (3.2.4)
+ bullet (6.1.0)
+ activesupport (>= 3.0.0)
+ uniform_notifier (~> 1.11)
+ byebug (11.1.3)
+ cbor (0.5.9.6)
+ certified (1.0.0)
+ chunky_png (1.3.14)
+ coderay (1.1.3)
+ colored2 (3.1.2)
+ concurrent-ruby (1.1.7)
+ connection_pool (2.2.3)
+ cose (1.2.0)
+ cbor (~> 0.5.9)
+ openssl-signature_algorithm (~> 1.0)
+ cppjieba_rb (0.3.3)
+ crack (0.4.4)
+ crass (1.0.6)
+ css_parser (1.7.1)
+ addressable
+ debug_inspector (0.0.3)
+ diff-lcs (1.4.4)
+ diffy (3.4.0)
+ discourse-ember-rails (0.18.6)
+ active_model_serializers
+ ember-data-source (>= 1.0.0.beta.5)
+ ember-handlebars-template (>= 0.1.1, < 1.0)
+ ember-source (>= 1.1.0)
+ jquery-rails (>= 1.0.17)
+ railties (>= 3.1)
+ discourse-ember-source (3.12.2.2)
+ discourse-fonts (0.0.5)
+ discourse_image_optim (0.26.2)
+ exifr (~> 1.2, >= 1.2.2)
+ fspath (~> 3.0)
+ image_size (~> 1.5)
+ in_threads (~> 1.3)
+ progress (~> 3.0, >= 3.0.1)
+ docile (1.3.2)
+ email_reply_trimmer (0.1.13)
+ ember-data-source (3.0.2)
+ ember-source (>= 2, < 3.0)
+ ember-handlebars-template (0.8.0)
+ barber (>= 0.11.0)
+ sprockets (>= 3.3, < 4.1)
+ ember-source (2.18.2)
+ erubi (1.10.0)
+ excon (0.78.0)
+ execjs (2.7.0)
+ exifr (1.3.9)
+ fabrication (2.21.1)
+ fakeweb (1.3.0)
+ faraday (1.1.0)
+ multipart-post (>= 1.2, < 3)
+ ruby2_keywords
+ fast_blank (1.0.0)
+ fast_xs (0.8.0)
+ fastimage (2.2.0)
+ ffi (1.13.1)
+ flamegraph (0.9.5)
+ fspath (3.1.2)
+ gc_tracer (1.5.1)
+ globalid (0.4.2)
+ activesupport (>= 4.2.0)
+ guess_html_encoding (0.0.11)
+ hashdiff (1.0.1)
+ hashie (4.1.0)
+ highline (2.0.3)
+ hkdf (0.3.0)
+ htmlentities (4.3.4)
+ http_accept_language (2.1.1)
+ i18n (1.8.5)
+ concurrent-ruby (~> 1.0)
+ image_size (1.5.0)
+ in_threads (1.5.4)
+ jmespath (1.4.0)
+ jquery-rails (4.4.0)
+ rails-dom-testing (>= 1, < 3)
+ railties (>= 4.2.0)
+ thor (>= 0.14, < 2.0)
+ json (2.3.1)
+ json-schema (2.8.1)
+ addressable (>= 2.4)
+ jwt (2.2.2)
+ kgio (2.11.3)
+ libv8 (8.4.255.0)
+ listen (3.3.1)
+ rb-fsevent (~> 0.10, >= 0.10.3)
+ rb-inotify (~> 0.9, >= 0.9.10)
+ lograge (0.11.2)
+ actionpack (>= 4)
+ activesupport (>= 4)
+ railties (>= 4)
+ request_store (~> 1.0)
+ logstash-event (1.2.02)
+ logstash-logger (0.26.1)
+ logstash-event (~> 1.2)
+ logster (2.9.4)
+ loofah (2.8.0)
+ crass (~> 1.0.2)
+ nokogiri (>= 1.5.9)
+ lru_redux (1.1.0)
+ lz4-ruby (0.3.3)
+ mail (2.7.1)
+ mini_mime (>= 0.1.1)
+ maxminddb (0.1.22)
+ memory_profiler (0.9.14)
+ message_bus (3.3.4)
+ rack (>= 1.1.3)
+ method_source (1.0.0)
+ mini_mime (1.0.2)
+ mini_portile2 (2.4.0)
+ mini_racer (0.3.1)
+ libv8 (~> 8.4.255)
+ mini_scheduler (0.12.3)
+ sidekiq
+ mini_sql (0.3)
+ mini_suffix (0.3.0)
+ ffi (~> 1.9)
+ minitest (5.14.2)
+ mocha (1.11.2)
+ mock_redis (0.26.0)
+ msgpack (1.3.3)
+ multi_json (1.15.0)
+ multi_xml (0.6.0)
+ multipart-post (2.1.1)
+ mustache (1.1.1)
+ nio4r (2.5.4)
+ nokogiri (1.10.10)
+ mini_portile2 (~> 2.4.0)
+ nokogumbo (2.0.2)
+ nokogiri (~> 1.8, >= 1.8.4)
+ oauth (0.5.4)
+ oauth2 (1.4.4)
+ faraday (>= 0.8, < 2.0)
+ jwt (>= 1.0, < 3.0)
+ multi_json (~> 1.3)
+ multi_xml (~> 0.5)
+ rack (>= 1.2, < 3)
+ oj (3.10.16)
+ omniauth (1.9.1)
+ hashie (>= 3.4.6)
+ rack (>= 1.6.2, < 3)
+ omniauth-facebook (8.0.0)
+ omniauth-oauth2 (~> 1.2)
+ omniauth-github (1.4.0)
+ omniauth (~> 1.5)
+ omniauth-oauth2 (>= 1.4.0, < 2.0)
+ omniauth-google-oauth2 (0.8.0)
+ jwt (>= 2.0)
+ omniauth (>= 1.1.1)
+ omniauth-oauth2 (>= 1.6)
+ omniauth-oauth (1.1.0)
+ oauth
+ omniauth (~> 1.0)
+ omniauth-oauth2 (1.7.0)
+ oauth2 (~> 1.4)
+ omniauth (~> 1.9)
+ omniauth-twitter (1.4.0)
+ omniauth-oauth (~> 1.1)
+ rack
+ onebox (2.2.1)
+ addressable (~> 2.7.0)
+ htmlentities (~> 4.3)
+ multi_json (~> 1.11)
+ mustache
+ nokogiri (~> 1.7)
+ sanitize
+ openssl-signature_algorithm (1.0.0)
+ optimist (3.0.1)
+ parallel (1.20.1)
+ parallel_tests (3.4.0)
+ parallel
+ parser (2.7.2.0)
+ ast (~> 2.4.1)
+ pg (1.2.3)
+ progress (3.5.2)
+ pry (0.13.1)
+ coderay (~> 1.1)
+ method_source (~> 1.0)
+ pry-byebug (3.9.0)
+ byebug (~> 11.0)
+ pry (~> 0.13.0)
+ pry-rails (0.3.9)
+ pry (>= 0.10.4)
+ public_suffix (4.0.6)
+ puma (5.0.4)
+ nio4r (~> 2.0)
+ r2 (0.2.7)
+ rack (2.2.3)
+ rack-mini-profiler (2.2.0)
+ rack (>= 1.2.0)
+ rack-protection (2.1.0)
+ rack
+ rack-test (1.1.0)
+ rack (>= 1.0, < 3)
+ rails-dom-testing (2.0.3)
+ activesupport (>= 4.2.0)
+ nokogiri (>= 1.6)
+ rails-html-sanitizer (1.3.0)
+ loofah (~> 2.3)
+ rails_failover (0.6.2)
+ activerecord (~> 6.0)
+ concurrent-ruby
+ railties (~> 6.0)
+ rails_multisite (2.5.0)
+ activerecord (> 5.0, < 7)
+ railties (> 5.0, < 7)
+ railties (6.0.3.3)
+ actionpack (= 6.0.3.3)
+ activesupport (= 6.0.3.3)
+ method_source
+ rake (>= 0.8.7)
+ thor (>= 0.20.3, < 2.0)
+ rainbow (3.0.0)
+ raindrops (0.19.1)
+ rake (13.0.1)
+ rb-fsevent (0.10.4)
+ rb-inotify (0.10.1)
+ ffi (~> 1.0)
+ rbtrace (0.4.14)
+ ffi (>= 1.0.6)
+ msgpack (>= 0.4.3)
+ optimist (>= 3.0.0)
+ rchardet (1.8.0)
+ redis (4.2.5)
+ redis-namespace (1.8.0)
+ redis (>= 3.0.4)
+ regexp_parser (2.0.0)
+ request_store (1.5.0)
+ rack (>= 1.4)
+ rexml (3.2.4)
+ rinku (2.0.6)
+ rotp (6.2.0)
+ rqrcode (1.1.2)
+ chunky_png (~> 1.0)
+ rqrcode_core (~> 0.1)
+ rqrcode_core (0.1.2)
+ rspec (3.10.0)
+ rspec-core (~> 3.10.0)
+ rspec-expectations (~> 3.10.0)
+ rspec-mocks (~> 3.10.0)
+ rspec-core (3.10.0)
+ rspec-support (~> 3.10.0)
+ rspec-expectations (3.10.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.10.0)
+ rspec-html-matchers (0.9.4)
+ nokogiri (~> 1)
+ rspec (>= 3.0.0.a, < 4)
+ rspec-mocks (3.10.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.10.0)
+ rspec-rails (4.0.1)
+ actionpack (>= 4.2)
+ activesupport (>= 4.2)
+ railties (>= 4.2)
+ rspec-core (~> 3.9)
+ rspec-expectations (~> 3.9)
+ rspec-mocks (~> 3.9)
+ rspec-support (~> 3.9)
+ rspec-support (3.10.0)
+ rswag-specs (2.3.1)
+ activesupport (>= 3.1, < 7.0)
+ json-schema (~> 2.2)
+ railties (>= 3.1, < 7.0)
+ rtlit (0.0.5)
+ rubocop (1.4.2)
+ parallel (~> 1.10)
+ parser (>= 2.7.1.5)
+ rainbow (>= 2.2.2, < 4.0)
+ regexp_parser (>= 1.8)
+ rexml
+ rubocop-ast (>= 1.1.1)
+ ruby-progressbar (~> 1.7)
+ unicode-display_width (>= 1.4.0, < 2.0)
+ rubocop-ast (1.2.0)
+ parser (>= 2.7.1.5)
+ rubocop-discourse (2.4.1)
+ rubocop (>= 1.1.0)
+ rubocop-rspec (>= 2.0.0)
+ rubocop-rspec (2.0.0)
+ rubocop (~> 1.0)
+ rubocop-ast (>= 1.1.0)
+ ruby-prof (1.4.2)
+ ruby-progressbar (1.10.1)
+ ruby-readability (0.7.0)
+ guess_html_encoding (>= 0.0.4)
+ nokogiri (>= 1.6.0)
+ ruby2_keywords (0.0.2)
+ rubyzip (2.3.0)
+ sanitize (5.2.1)
+ crass (~> 1.0.2)
+ nokogiri (>= 1.8.0)
+ nokogumbo (~> 2.0)
+ sassc (2.0.1)
+ ffi (~> 1.9)
+ rake
+ sassc-rails (2.1.2)
+ railties (>= 4.0.0)
+ sassc (>= 2.0)
+ sprockets (> 3.0)
+ sprockets-rails
+ tilt
+ seed-fu (2.3.9)
+ activerecord (>= 3.1)
+ activesupport (>= 3.1)
+ shoulda-matchers (4.4.1)
+ activesupport (>= 4.2.0)
+ sidekiq (6.1.2)
+ connection_pool (>= 2.2.2)
+ rack (~> 2.0)
+ redis (>= 4.2.0)
+ simplecov (0.20.0)
+ docile (~> 1.1)
+ simplecov-html (~> 0.11)
+ simplecov_json_formatter (~> 0.1)
+ simplecov-html (0.12.3)
+ simplecov_json_formatter (0.1.2)
+ sprockets (3.7.2)
+ concurrent-ruby (~> 1.0)
+ rack (> 1, < 3)
+ sprockets-rails (3.2.2)
+ actionpack (>= 4.0)
+ activesupport (>= 4.0)
+ sprockets (>= 3.0.0)
+ sshkey (2.0.0)
+ stackprof (0.2.16)
+ test-prof (0.12.2)
+ thor (1.0.1)
+ thread_safe (0.3.6)
+ tilt (2.0.10)
+ tzinfo (1.2.8)
+ thread_safe (~> 0.1)
+ uglifier (4.2.0)
+ execjs (>= 0.3.0, < 3)
+ unf (0.1.4)
+ unf_ext
+ unf_ext (0.0.7.7)
+ unicode-display_width (1.7.0)
+ unicorn (5.7.0)
+ kgio (~> 2.6)
+ raindrops (~> 0.7)
+ uniform_notifier (1.13.0)
+ webmock (3.10.0)
+ addressable (>= 2.3.6)
+ crack (>= 0.3.2)
+ hashdiff (>= 0.4.0, < 2.0.0)
+ webpush (1.1.0)
+ hkdf (~> 0.2)
+ jwt (~> 2.0)
+ xorcist (1.1.2)
+ yaml-lint (0.0.10)
+ zeitwerk (2.4.1)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ actionmailer (= 6.0.3.3)
+ actionpack (= 6.0.3.3)
+ actionview (= 6.0.3.3)
+ actionview_precompiler
+ active_model_serializers (~> 0.8.3)
+ activemodel (= 6.0.3.3)
+ activerecord (= 6.0.3.3)
+ activesupport (= 6.0.3.3)
+ addressable
+ annotate
+ aws-sdk-s3
+ aws-sdk-sns
+ barber
+ better_errors
+ binding_of_caller
+ bootsnap
+ bullet
+ byebug
+ cbor
+ certified
+ colored2
+ cose
+ cppjieba_rb
+ css_parser
+ diffy
+ discourse-ember-rails (= 0.18.6)
+ discourse-ember-source (~> 3.12.2)
+ discourse-fonts
+ discourse_image_optim
+ email_reply_trimmer
+ ember-handlebars-template (= 0.8.0)
+ excon
+ execjs
+ fabrication
+ fakeweb
+ fast_blank
+ fast_xs
+ fastimage
+ flamegraph
+ gc_tracer
+ highline
+ htmlentities
+ http_accept_language
+ json
+ listen
+ lograge
+ logstash-event
+ logstash-logger
+ logster
+ lru_redux
+ lz4-ruby
+ mail
+ maxminddb
+ memory_profiler
+ message_bus
+ mini_mime
+ mini_racer
+ mini_scheduler
+ mini_sql
+ mini_suffix
+ minitest
+ mocha
+ mock_redis
+ multi_json
+ mustache
+ nokogiri
+ oj
+ omniauth
+ omniauth-facebook
+ omniauth-github
+ omniauth-google-oauth2
+ omniauth-oauth2
+ omniauth-twitter
+ onebox
+ parallel_tests
+ pg
+ pry-byebug
+ pry-rails
+ puma
+ r2
+ rack
+ rack-mini-profiler
+ rack-protection
+ rails_failover
+ rails_multisite
+ railties (= 6.0.3.3)
+ rake
+ rb-fsevent
+ rbtrace
+ rchardet
+ redis
+ redis-namespace
+ rinku
+ rotp
+ rqrcode
+ rspec
+ rspec-html-matchers
+ rspec-rails
+ rswag-specs
+ rtlit
+ rubocop-discourse
+ ruby-prof
+ ruby-readability
+ rubyzip
+ sassc (= 2.0.1)
+ sassc-rails
+ seed-fu
+ shoulda-matchers
+ sidekiq
+ simplecov
+ sprockets (= 3.7.2)
+ sprockets-rails
+ sshkey
+ stackprof
+ test-prof
+ thor
+ uglifier
+ unf
+ unicorn
+ webmock
+ webpush
+ xorcist
+ yaml-lint
+
+BUNDLED WITH
+ 2.1.4
diff --git a/pkgs/servers/web-apps/discourse/rubyEnv/gemset.nix b/pkgs/servers/web-apps/discourse/rubyEnv/gemset.nix
new file mode 100644
index 000000000000..b2cf191a4444
--- /dev/null
+++ b/pkgs/servers/web-apps/discourse/rubyEnv/gemset.nix
@@ -0,0 +1,2272 @@
+{
+ actionmailer = {
+ dependencies = ["actionpack" "actionview" "activejob" "mail" "rails-dom-testing"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1spq0dbfn0qkqg9sq0rsjn360b4j36zly8hawaivkrwr3rsvyz75";
+ type = "gem";
+ };
+ version = "6.0.3.3";
+ };
+ actionpack = {
+ dependencies = ["actionview" "activesupport" "rack" "rack-test" "rails-dom-testing" "rails-html-sanitizer"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1p873nqwmpsvmkb5n86d70wndx1qhy15pc9mbcd1mc8sj174578b";
+ type = "gem";
+ };
+ version = "6.0.3.3";
+ };
+ actionview = {
+ dependencies = ["activesupport" "builder" "erubi" "rails-dom-testing" "rails-html-sanitizer"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "08pvmjddlw01q5r9zdfgddwp4csndpf5i2w47677z5r36jznz36q";
+ type = "gem";
+ };
+ version = "6.0.3.3";
+ };
+ actionview_precompiler = {
+ dependencies = ["actionview"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "07dx8vkwig8han4zccs0chahcf9ibd4abzx9n56qah8zak5cyrhd";
+ type = "gem";
+ };
+ version = "0.2.3";
+ };
+ active_model_serializers = {
+ dependencies = ["activemodel"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0k3mgia2ahh7mbk30hjq9pzqbk0kh281s91kq2z6p555nv9y6l3k";
+ type = "gem";
+ };
+ version = "0.8.4";
+ };
+ activejob = {
+ dependencies = ["activesupport" "globalid"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0w54ckvc229iaax879hkhyc93j7z8p0v7acp6mk3h8xjfvmwy5jp";
+ type = "gem";
+ };
+ version = "6.0.3.3";
+ };
+ activemodel = {
+ dependencies = ["activesupport"];
+ groups = ["default" "development"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "166jlx6kbby01vr37srh081a9fykgsz873yg5i9gl2ar3vw9gs56";
+ type = "gem";
+ };
+ version = "6.0.3.3";
+ };
+ activerecord = {
+ dependencies = ["activemodel" "activesupport"];
+ groups = ["default" "development"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0y2a4ss6ld6yrhpcbcb3kjn5gj6zk9qklp2aq5rl1awl8vbdbdb7";
+ type = "gem";
+ };
+ version = "6.0.3.3";
+ };
+ activesupport = {
+ dependencies = ["concurrent-ruby" "i18n" "minitest" "tzinfo" "zeitwerk"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1dmkqbvndbz011a1byg6f990936vfadbnwjwjw9vjzr4kd8bxk96";
+ type = "gem";
+ };
+ version = "6.0.3.3";
+ };
+ addressable = {
+ dependencies = ["public_suffix"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1fvchp2rhp2rmigx7qglf69xvjqvzq7x0g49naliw29r2bz656sy";
+ type = "gem";
+ };
+ version = "2.7.0";
+ };
+ annotate = {
+ dependencies = ["activerecord" "rake"];
+ groups = ["development"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1dxrfppwfg13vqmambbs56xjj8qsdgcy58r2yc44vvy3z1g5yflw";
+ type = "gem";
+ };
+ version = "3.1.1";
+ };
+ ast = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1l3468czzjmxl93ap40hp7z94yxp4nbag0bxqs789bm30md90m2a";
+ type = "gem";
+ };
+ version = "2.4.1";
+ };
+ aws-eventstream = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0r0pn66yqrdkrfdin7qdim0yj2x75miyg4wp6mijckhzhrjb7cv5";
+ type = "gem";
+ };
+ version = "1.1.0";
+ };
+ aws-partitions = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "17xranmng1mg6238zdmnfvaig82r2ymp2apra9yh5d8rhvn8hkwm";
+ type = "gem";
+ };
+ version = "1.390.0";
+ };
+ aws-sdk-core = {
+ dependencies = ["aws-eventstream" "aws-partitions" "aws-sigv4" "jmespath"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "05dq7jfv5qf1y00ib96nqsipf08hflw8n8fwkyjw4qav84wjqaq4";
+ type = "gem";
+ };
+ version = "3.109.2";
+ };
+ aws-sdk-kms = {
+ dependencies = ["aws-sdk-core" "aws-sigv4"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0ly1m631qm2ciif7sysbzrgczjvz95ga3g6w6vrzvfdv31jjnl9a";
+ type = "gem";
+ };
+ version = "1.39.0";
+ };
+ aws-sdk-s3 = {
+ dependencies = ["aws-sdk-core" "aws-sdk-kms" "aws-sigv4"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1mld0yh6q6i2nbb143g5xc6gm70sqpvpwxfknlihrd8jmw3xc0bs";
+ type = "gem";
+ };
+ version = "1.83.2";
+ };
+ aws-sdk-sns = {
+ dependencies = ["aws-sdk-core" "aws-sigv4"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1dw80ldqhb1mny5irgi2jh36hykcmyd07xalv21xncxqzmf8aiag";
+ type = "gem";
+ };
+ version = "1.35.0";
+ };
+ aws-sigv4 = {
+ dependencies = ["aws-eventstream"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1ll9382c1x2hp750cilh01h1cycgyhdr4cmmgx23k94hyyb8chv5";
+ type = "gem";
+ };
+ version = "1.2.2";
+ };
+ barber = {
+ dependencies = ["ember-source" "execjs"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "07rnlbh7kgamcbnl1sqlcdrjj8src4qc687klqq4a3vqq2slnscx";
+ type = "gem";
+ };
+ version = "0.12.2";
+ };
+ better_errors = {
+ dependencies = ["coderay" "erubi" "rack"];
+ groups = ["development"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "11220lfzhsyf5fcril3qd689kgg46qlpiiaj00hc9mh4mcbc3vrr";
+ type = "gem";
+ };
+ version = "2.9.1";
+ };
+ binding_of_caller = {
+ dependencies = ["debug_inspector"];
+ groups = ["development"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "05syqlks7463zsy1jdfbbdravdhj9hpj5pv2m74blqpv8bq4vv5g";
+ type = "gem";
+ };
+ version = "0.8.0";
+ };
+ bootsnap = {
+ dependencies = ["msgpack"];
+ groups = ["default"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1qx1f729bgh391agsqb4ngzn22wdn4cc6mkp0cipf0d5hsg9cpaq";
+ type = "gem";
+ };
+ version = "1.5.1";
+ };
+ builder = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "045wzckxpwcqzrjr353cxnyaxgf0qg22jh00dcx7z38cys5g1jlr";
+ type = "gem";
+ };
+ version = "3.2.4";
+ };
+ bullet = {
+ dependencies = ["activesupport" "uniform_notifier"];
+ groups = ["development"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "18ifwnvn13755qkfigapyj5bflpby3phxzbb7x5336d0kzv5k7d9";
+ type = "gem";
+ };
+ version = "6.1.0";
+ };
+ byebug = {
+ groups = ["development" "test"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0nx3yjf4xzdgb8jkmk2344081gqr22pgjqnmjg2q64mj5d6r9194";
+ type = "gem";
+ };
+ version = "11.1.3";
+ };
+ cbor = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0511idr8xps9625nh3kxr68sdy6l3xy2kcz7r57g47fxb1v18jj3";
+ type = "gem";
+ };
+ version = "0.5.9.6";
+ };
+ certified = {
+ groups = ["development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1706p6p0a8adyvd943af2a3093xakvislgffw3v9dvp7j07dyk5a";
+ type = "gem";
+ };
+ version = "1.0.0";
+ };
+ chunky_png = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1v52ndgx9r4jybq8yzr8anzfbnjk4y2hvz97nm9924wi4bad3xkf";
+ type = "gem";
+ };
+ version = "1.3.14";
+ };
+ coderay = {
+ groups = ["default" "development"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0jvxqxzply1lwp7ysn94zjhh57vc14mcshw1ygw14ib8lhc00lyw";
+ type = "gem";
+ };
+ version = "1.1.3";
+ };
+ colored2 = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0jlbqa9q4mvrm73aw9mxh23ygzbjiqwisl32d8szfb5fxvbjng5i";
+ type = "gem";
+ };
+ version = "3.1.2";
+ };
+ concurrent-ruby = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1vnxrbhi7cq3p4y2v9iwd10v1c7l15is4var14hwnb2jip4fyjzz";
+ type = "gem";
+ };
+ version = "1.1.7";
+ };
+ connection_pool = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1qikl4av1z8kqnk5ba18136dpqzw8wjawc2w9b4zb5psdd5z8nwf";
+ type = "gem";
+ };
+ version = "2.2.3";
+ };
+ cose = {
+ dependencies = ["cbor" "openssl-signature_algorithm"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1gx239d2fracq9az74wfdwmp5zm7zpzkcgchwnv2ng33d8r33p3m";
+ type = "gem";
+ };
+ version = "1.2.0";
+ };
+ cppjieba_rb = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1sslff7yy8jvp4rcn1b6jn9v0d3iibb68i79shgd94rs2yq8k117";
+ type = "gem";
+ };
+ version = "0.3.3";
+ };
+ crack = {
+ groups = ["default" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1awi8jy4jn0f7vxpdvz3xvn1zzjbjh33n28lfkijh77dla5zb7lc";
+ type = "gem";
+ };
+ version = "0.4.4";
+ };
+ crass = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0pfl5c0pyqaparxaqxi6s4gfl21bdldwiawrc0aknyvflli60lfw";
+ type = "gem";
+ };
+ version = "1.0.6";
+ };
+ css_parser = {
+ dependencies = ["addressable"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "04c4dl8cm5rjr50k9qa6yl9r05fk9zcb1zxh0y0cdahxlsgcydfw";
+ type = "gem";
+ };
+ version = "1.7.1";
+ };
+ debug_inspector = {
+ groups = ["default" "development"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0vxr0xa1mfbkfcrn71n7c4f2dj7la5hvphn904vh20j3x4j5lrx0";
+ type = "gem";
+ };
+ version = "0.0.3";
+ };
+ diff-lcs = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0m925b8xc6kbpnif9dldna24q1szg4mk0fvszrki837pfn46afmz";
+ type = "gem";
+ };
+ version = "1.4.4";
+ };
+ diffy = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0nrg7kpgz6cn1gv2saj2fa5sfiykamvd7vn9lw2v625k7pjwf31l";
+ type = "gem";
+ };
+ version = "3.4.0";
+ };
+ discourse-ember-rails = {
+ dependencies = ["active_model_serializers" "ember-data-source" "ember-handlebars-template" "ember-source" "jquery-rails" "railties"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0ax5x2d6q6hkm7r58ai9p0sahlg842aqlm7dpv6svrfpnjlaz7sf";
+ type = "gem";
+ };
+ version = "0.18.6";
+ };
+ discourse-ember-source = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0mqkwiqb5n64lc5jdjnmpgb9apq08ywkz9yk8mj1sx2lqcsw11pc";
+ type = "gem";
+ };
+ version = "3.12.2.2";
+ };
+ discourse-fonts = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0xhwgqclh3jncjr55m0hyq3w3iw8jw2r7ickzq1zn1282pc3n2i7";
+ type = "gem";
+ };
+ version = "0.0.5";
+ };
+ discourse_image_optim = {
+ dependencies = ["exifr" "fspath" "image_size" "in_threads" "progress"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "11nqmga5ygxyhjmsc07gsa0fwwyhdpwi20yyr4fnh263xs1xylvv";
+ type = "gem";
+ };
+ version = "0.26.2";
+ };
+ docile = {
+ groups = ["default" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0qrwiyagxzl8zlx3dafb0ay8l14ib7imb2rsmx70i5cp420v8gif";
+ type = "gem";
+ };
+ version = "1.3.2";
+ };
+ email_reply_trimmer = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1jgcxifm48xq5dz9k47q43pqm5bfnf14l62l3bqhmv8f6z8dw4ki";
+ type = "gem";
+ };
+ version = "0.1.13";
+ };
+ ember-data-source = {
+ dependencies = ["ember-source"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1803nh3knvwl12h63jd48qvbbrp42yy291wcb35960daklip0fd8";
+ type = "gem";
+ };
+ version = "3.0.2";
+ };
+ ember-handlebars-template = {
+ dependencies = ["barber" "sprockets"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1wxj3vi4xs3vjxrdbzi4j4w6vv45r5dkz2rg2ldid3p8dp3irlf4";
+ type = "gem";
+ };
+ version = "0.8.0";
+ };
+ ember-source = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0sixy30ym9j2slhlr0lfq943g958w8arlb0lsizh59iv1w5gmxxy";
+ type = "gem";
+ };
+ version = "2.18.2";
+ };
+ erubi = {
+ groups = ["default" "development" "test"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "09l8lz3j00m898li0yfsnb6ihc63rdvhw3k5xczna5zrjk104f2l";
+ type = "gem";
+ };
+ version = "1.10.0";
+ };
+ excon = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1hi89v53pm2abfv9j8lgqdd7hgkr7fr0gwrczr940iwbb3xv7rrs";
+ type = "gem";
+ };
+ version = "0.78.0";
+ };
+ execjs = {
+ groups = ["assets" "default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1yz55sf2nd3l666ms6xr18sm2aggcvmb8qr3v53lr4rir32y1yp1";
+ type = "gem";
+ };
+ version = "2.7.0";
+ };
+ exifr = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0mylhwmh6n4xihxr9s3zj0lc286f5maxbqd4dgk3paqnd7afz88s";
+ type = "gem";
+ };
+ version = "1.3.9";
+ };
+ fabrication = {
+ groups = ["development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1pdrl55xf76pbc5kjzp7diawxxvgbk2cm38532in6df823431n6z";
+ type = "gem";
+ };
+ version = "2.21.1";
+ };
+ fakeweb = {
+ groups = ["test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1a09z9nb369bvwpghncgd5y4f95lh28w0q258srh02h22fz9dj8y";
+ type = "gem";
+ };
+ version = "1.3.0";
+ };
+ faraday = {
+ dependencies = ["multipart-post" "ruby2_keywords"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "16dapwi5pivrl25r4lkr1mxjrzkznj4wlcb08fzkmxnj4g5c6y35";
+ type = "gem";
+ };
+ version = "1.1.0";
+ };
+ fast_blank = {
+ groups = ["default"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "rbx";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "16s1ilyvwzmkcgmklbrn0c2pch5n02vf921njx0bld4crgdr6z56";
+ type = "gem";
+ };
+ version = "1.0.0";
+ };
+ fast_xs = {
+ groups = ["default"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "rbx";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1iydzaqmvqq7ncxkr182aybkk6xap0cb2w9amr73vbdxi2qf3wjz";
+ type = "gem";
+ };
+ version = "0.8.0";
+ };
+ fastimage = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "11ny2pj0j6pljszrf1w3iqdv2pcl2iwwghjbgcjlizy424zbh0hb";
+ type = "gem";
+ };
+ version = "2.2.0";
+ };
+ ffi = {
+ groups = ["default" "development" "test"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "12lpwaw82bb0rm9f52v1498bpba8aj2l2q359mkwbxsswhpga5af";
+ type = "gem";
+ };
+ version = "1.13.1";
+ };
+ flamegraph = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1p785nmhdzbwj0qpxn5fzrmr4kgimcds83v4f95f387z6w3050x6";
+ type = "gem";
+ };
+ version = "0.9.5";
+ };
+ fspath = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0xcxikkrjv8ws328nn5ax5pyfjs8pn7djg1hks7qyb3yp6prpb5m";
+ type = "gem";
+ };
+ version = "3.1.2";
+ };
+ gc_tracer = {
+ groups = ["default"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1yv3mp8lx74lfzs04fd5h4g89209iwhzpc407y35p7cmzgx6a4kv";
+ type = "gem";
+ };
+ version = "1.5.1";
+ };
+ globalid = {
+ dependencies = ["activesupport"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1zkxndvck72bfw235bd9nl2ii0lvs5z88q14706cmn702ww2mxv1";
+ type = "gem";
+ };
+ version = "0.4.2";
+ };
+ guess_html_encoding = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "16700fk6kmif3q3kpc1ldhy3nsc9pkxlgl8sqhznff2zjj5lddna";
+ type = "gem";
+ };
+ version = "0.0.11";
+ };
+ hashdiff = {
+ groups = ["default" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1nynpl0xbj0nphqx1qlmyggq58ms1phf5i03hk64wcc0a17x1m1c";
+ type = "gem";
+ };
+ version = "1.0.1";
+ };
+ hashie = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "02bsx12ihl78x0vdm37byp78jjw2ff6035y7rrmbd90qxjwxr43q";
+ type = "gem";
+ };
+ version = "4.1.0";
+ };
+ highline = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0yclf57n2j3cw8144ania99h1zinf8q3f5zrhqa754j6gl95rp9d";
+ type = "gem";
+ };
+ version = "2.0.3";
+ };
+ hkdf = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "04fixg0a51n4vy0j6c1hvisa2yl33m3jrrpxpb5sq6j511vjriil";
+ type = "gem";
+ };
+ version = "0.3.0";
+ };
+ htmlentities = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1nkklqsn8ir8wizzlakncfv42i32wc0w9hxp00hvdlgjr7376nhj";
+ type = "gem";
+ };
+ version = "4.3.4";
+ };
+ http_accept_language = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0d0nlfz9vm4jr1l6q0chx4rp2hrnrfbx3gadc1dz930lbbaz0hq0";
+ type = "gem";
+ };
+ version = "2.1.1";
+ };
+ i18n = {
+ dependencies = ["concurrent-ruby"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "153sx77p16vawrs4qpkv7qlzf9v5fks4g7xqcj1dwk40i6g7rfzk";
+ type = "gem";
+ };
+ version = "1.8.5";
+ };
+ image_size = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0zrn2mqaf1kk548wn1y35i1a6kwh3320q62m929kn9m8sqpy4fk7";
+ type = "gem";
+ };
+ version = "1.5.0";
+ };
+ in_threads = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0m71806p1gm4kxiz4gvkyr8qip16hifn2kdf926jz44jj6kc6bbs";
+ type = "gem";
+ };
+ version = "1.5.4";
+ };
+ jmespath = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1d4wac0dcd1jf6kc57891glih9w57552zgqswgy74d1xhgnk0ngf";
+ type = "gem";
+ };
+ version = "1.4.0";
+ };
+ jquery-rails = {
+ dependencies = ["rails-dom-testing" "railties" "thor"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0dkhm8lan1vnyl3ll0ks2q06576pdils8a1dr354vfc1y5dqw15i";
+ type = "gem";
+ };
+ version = "4.4.0";
+ };
+ json = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "158fawfwmv2sq4whqqaksfykkiad2xxrrj0nmpnc6vnlzi1bp7iz";
+ type = "gem";
+ };
+ version = "2.3.1";
+ };
+ json-schema = {
+ dependencies = ["addressable"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1yv5lfmr2nzd14af498xqd5p89f3g080q8wk0klr3vxgypsikkb5";
+ type = "gem";
+ };
+ version = "2.8.1";
+ };
+ jwt = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "14ynyq1q483spj20ffl4xayfqx1a8qr761mqjfxczf8lwlap392n";
+ type = "gem";
+ };
+ version = "2.2.2";
+ };
+ kgio = {
+ groups = ["default"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "rbx";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0ai6bzlvxbzpdl466p1qi4dlhx8ri2wcrp6x1l19y3yfs3a29rng";
+ type = "gem";
+ };
+ version = "2.11.3";
+ };
+ libv8 = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0317sr3nrl51sp844bps71smkrwim3fjn47wdfpbycixnbxspivm";
+ type = "gem";
+ };
+ version = "8.4.255.0";
+ };
+ listen = {
+ dependencies = ["rb-fsevent" "rb-inotify"];
+ groups = ["development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0028p1fss6pvw4mlpjqdmxfzsm8ww79irsadbibrr7f23qfn8ykr";
+ type = "gem";
+ };
+ version = "3.3.1";
+ };
+ lograge = {
+ dependencies = ["actionpack" "activesupport" "railties" "request_store"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1vrjm4yqn5l6q5gsl72fmk95fl6j9z1a05gzbrwmsm3gp1a1bgac";
+ type = "gem";
+ };
+ version = "0.11.2";
+ };
+ logstash-event = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1bk7fhhryjxp1klr3hq6i6srrc21wl4p980bysjp0w66z9hdr9w9";
+ type = "gem";
+ };
+ version = "1.2.02";
+ };
+ logstash-logger = {
+ dependencies = ["logstash-event"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1nh0jgz4rl46axqb9l0fa866kh34wb7yf11qc3j30xhprdqb8yjp";
+ type = "gem";
+ };
+ version = "0.26.1";
+ };
+ logster = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1ldikj3p0bakxg57didaw05pldjn0i5r20zawhqa34knlsqm66r6";
+ type = "gem";
+ };
+ version = "2.9.4";
+ };
+ loofah = {
+ dependencies = ["crass" "nokogiri"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0ndimir6k3kfrh8qrb7ir1j836l4r3qlwyclwjh88b86clblhszh";
+ type = "gem";
+ };
+ version = "2.8.0";
+ };
+ lru_redux = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1yxghzg7476sivz8yyr9nkak2dlbls0b89vc2kg52k0nmg6d0wgf";
+ type = "gem";
+ };
+ version = "1.1.0";
+ };
+ lz4-ruby = {
+ groups = ["default"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "rbx";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "12fymsvcb9kw6ycyfzc8b9svriq0afqf1qnl121xrz8c4gpfa6q1";
+ type = "gem";
+ };
+ version = "0.3.3";
+ };
+ mail = {
+ dependencies = ["mini_mime"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "00wwz6ys0502dpk8xprwcqfwyf3hmnx6lgxaiq6vj43mkx43sapc";
+ type = "gem";
+ };
+ version = "2.7.1";
+ };
+ maxminddb = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0zlhqilyggiryywgswfi624bv10qnkm66hggmg79vvgv73j3p4sh";
+ type = "gem";
+ };
+ version = "0.1.22";
+ };
+ memory_profiler = {
+ groups = ["default"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "04ivhv1bilwqm33jv28gar2vwzsichb5nipaq395d3axabv8qmfy";
+ type = "gem";
+ };
+ version = "0.9.14";
+ };
+ message_bus = {
+ dependencies = ["rack"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0hckijk9aa628nx66vr7axfsk7zfdkskaxj1mdzikk019q3h54fr";
+ type = "gem";
+ };
+ version = "3.3.4";
+ };
+ method_source = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1pnyh44qycnf9mzi1j6fywd5fkskv3x7nmsqrrws0rjn5dd4ayfp";
+ type = "gem";
+ };
+ version = "1.0.0";
+ };
+ mini_mime = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1axm0rxyx3ss93wbmfkm78a6x03l8y4qy60rhkkiq0aza0vwq3ha";
+ type = "gem";
+ };
+ version = "1.0.2";
+ };
+ mini_portile2 = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "15zplpfw3knqifj9bpf604rb3wc1vhq6363pd6lvhayng8wql5vy";
+ type = "gem";
+ };
+ version = "2.4.0";
+ };
+ mini_racer = {
+ dependencies = ["libv8"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0r7j241mvhyyc017bqgp0pvf3jyrwbcqvz2pzm0r8zn2r85ks1jl";
+ type = "gem";
+ };
+ version = "0.3.1";
+ };
+ mini_scheduler = {
+ dependencies = ["sidekiq"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0vigv7f1q5bkcb55ab2lyhq15yqfkg5mq61p7m7mw9b3jac7qjz1";
+ type = "gem";
+ };
+ version = "0.12.3";
+ };
+ mini_sql = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0qi4bj5jkh3673ybsxvsf7y485znyxb72vxg84gk9x65mf0y0m6h";
+ type = "gem";
+ };
+ version = "0.3";
+ };
+ mini_suffix = {
+ dependencies = ["ffi"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0bxd1fgzb20gvfvhbkrxym9fr7skm5x6fzvqfg4a0jijb34ww50h";
+ type = "gem";
+ };
+ version = "0.3.0";
+ };
+ minitest = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "170y2cvx51gm3cm3nhdf7j36sxnkh6vv8ls36p90ric7w8w16h4v";
+ type = "gem";
+ };
+ version = "5.14.2";
+ };
+ mocha = {
+ groups = ["development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0hxmkm8qxd04vwj8mqnpyrf2dwy7g1k9zipdfhl4y71cw7ijm9n4";
+ type = "gem";
+ };
+ version = "1.11.2";
+ };
+ mock_redis = {
+ groups = ["development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "06yj6j9x4zjckah4ixiwhy3hb6xzjp7yk7lmmcvcb8hpd0z0x95q";
+ type = "gem";
+ };
+ version = "0.26.0";
+ };
+ msgpack = {
+ groups = ["default"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1lva6bkvb4mfa0m3bqn4lm4s4gi81c40jvdcsrxr6vng49q9daih";
+ type = "gem";
+ };
+ version = "1.3.3";
+ };
+ multi_json = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0pb1g1y3dsiahavspyzkdy39j4q377009f6ix0bh1ag4nqw43l0z";
+ type = "gem";
+ };
+ version = "1.15.0";
+ };
+ multi_xml = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0lmd4f401mvravi1i1yq7b2qjjli0yq7dfc4p1nj5nwajp7r6hyj";
+ type = "gem";
+ };
+ version = "0.6.0";
+ };
+ multipart-post = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1zgw9zlwh2a6i1yvhhc4a84ry1hv824d6g2iw2chs3k5aylpmpfj";
+ type = "gem";
+ };
+ version = "2.1.1";
+ };
+ mustache = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1l0p4wx15mi3wnamfv92ipkia4nsx8qi132c6g51jfdma3fiz2ch";
+ type = "gem";
+ };
+ version = "1.1.1";
+ };
+ nio4r = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1cbwp1kbv6b2qfxv8sarv0d0ilb257jihlvdqj8f5pdm0ksq1sgk";
+ type = "gem";
+ };
+ version = "2.5.4";
+ };
+ nokogiri = {
+ dependencies = ["mini_portile2"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0xmf60nj5kg9vaj5bysy308687sgmkasgx06vbbnf94p52ih7si2";
+ type = "gem";
+ };
+ version = "1.10.10";
+ };
+ nokogumbo = {
+ dependencies = ["nokogiri"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0sxjnpjvrn10gdmfw2dimhch861lz00f28hvkkz0b1gc2rb65k9s";
+ type = "gem";
+ };
+ version = "2.0.2";
+ };
+ oauth = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1zszdg8q1b135z7l7crjj234k4j0m347hywp5kj6zsq7q78pw09y";
+ type = "gem";
+ };
+ version = "0.5.4";
+ };
+ oauth2 = {
+ dependencies = ["faraday" "jwt" "multi_json" "multi_xml" "rack"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1bhakjh30vi8scqwnhd1c9qkac9r8hh2lr0dbs5ynwmrc5djxknm";
+ type = "gem";
+ };
+ version = "1.4.4";
+ };
+ oj = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1xqmzqldi9a0wpilwx87yh61xd7647gg8ffammg4ava0bsx375g2";
+ type = "gem";
+ };
+ version = "3.10.16";
+ };
+ omniauth = {
+ dependencies = ["hashie" "rack"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "002vi9gwamkmhf0dsj2im1d47xw2n1jfhnzl18shxf3ampkqfmyz";
+ type = "gem";
+ };
+ version = "1.9.1";
+ };
+ omniauth-facebook = {
+ dependencies = ["omniauth-oauth2"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1z0f5sr2ddnvfva0jrfd4926nlv4528rfj7z595288n39304r092";
+ type = "gem";
+ };
+ version = "8.0.0";
+ };
+ omniauth-github = {
+ dependencies = ["omniauth" "omniauth-oauth2"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0xbk0dbxqfpyfb33ghz6vrlz3m6442rp18ryf13gwzlnifcawhlb";
+ type = "gem";
+ };
+ version = "1.4.0";
+ };
+ omniauth-google-oauth2 = {
+ dependencies = ["jwt" "omniauth" "omniauth-oauth2"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "17pgqasl048irs2c6w6g57zvk0ygb5ml1krwir4qi4b6y53zyr55";
+ type = "gem";
+ };
+ version = "0.8.0";
+ };
+ omniauth-oauth = {
+ dependencies = ["oauth" "omniauth"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1n5vk4by7hkyc09d9blrw2argry5awpw4gbw1l4n2s9b3j4qz037";
+ type = "gem";
+ };
+ version = "1.1.0";
+ };
+ omniauth-oauth2 = {
+ dependencies = ["oauth2" "omniauth"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0v6rw7sd223k7qw0l13wikgfcqbvbk81r53a9i2z0k7jl5vd97w5";
+ type = "gem";
+ };
+ version = "1.7.0";
+ };
+ omniauth-twitter = {
+ dependencies = ["omniauth-oauth" "rack"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0r5j65hkpgzhvvbs90id3nfsjgsad6ymzggbm7zlaxvnrmvnrk65";
+ type = "gem";
+ };
+ version = "1.4.0";
+ };
+ onebox = {
+ dependencies = ["addressable" "htmlentities" "multi_json" "mustache" "nokogiri" "sanitize"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0b2aih0d5cva9bris36gh1mk3ym61wgxlpwvzjd6qphdrjfzqx8v";
+ type = "gem";
+ };
+ version = "2.2.1";
+ };
+ openssl-signature_algorithm = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0h1pfx49j8d9vbdbi8jyj0mr63l7rhflgvgc0nhfygm1v77d7nkn";
+ type = "gem";
+ };
+ version = "1.0.0";
+ };
+ optimist = {
+ groups = ["default"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1vg2chy1cfmdj6c1gryl8zvjhhmb3plwgyh1jfnpq4fnfqv7asrk";
+ type = "gem";
+ };
+ version = "3.0.1";
+ };
+ parallel = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0055br0mibnqz0j8wvy20zry548dhkakws681bhj3ycb972awkzd";
+ type = "gem";
+ };
+ version = "1.20.1";
+ };
+ parallel_tests = {
+ dependencies = ["parallel"];
+ groups = ["development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1mvdk8vgzqjv2pvadxwc8w2vf8dmiw145rjf47c36nn6l5hh02j6";
+ type = "gem";
+ };
+ version = "3.4.0";
+ };
+ parser = {
+ dependencies = ["ast"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1f7gmm60yla325wlnd3qkxs59qm2y0aan8ljpg6k18rwzrrfil6z";
+ type = "gem";
+ };
+ version = "2.7.2.0";
+ };
+ pg = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "13mfrysrdrh8cka1d96zm0lnfs59i5x2g6ps49r2kz5p3q81xrzj";
+ type = "gem";
+ };
+ version = "1.2.3";
+ };
+ progress = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1pm3bv5n8c8j0vfm7wghd7xf6yq4m068cksxjldmna11qi0h0s8s";
+ type = "gem";
+ };
+ version = "3.5.2";
+ };
+ pry = {
+ dependencies = ["coderay" "method_source"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0iyw4q4an2wmk8v5rn2ghfy2jaz9vmw2nk8415nnpx2s866934qk";
+ type = "gem";
+ };
+ version = "0.13.1";
+ };
+ pry-byebug = {
+ dependencies = ["byebug" "pry"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "096y5vmzpyy4x9h4ky4cs4y7d19vdq9vbwwrqafbh5gagzwhifiv";
+ type = "gem";
+ };
+ version = "3.9.0";
+ };
+ pry-rails = {
+ dependencies = ["pry"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1cf4ii53w2hdh7fn8vhqpzkymmchjbwij4l3m7s6fsxvb9bn51j6";
+ type = "gem";
+ };
+ version = "0.3.9";
+ };
+ public_suffix = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1xqcgkl7bwws1qrlnmxgh8g4g9m10vg60bhlw40fplninb3ng6d9";
+ type = "gem";
+ };
+ version = "4.0.6";
+ };
+ puma = {
+ dependencies = ["nio4r"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0mkmfbf4qyiknwi9bb5432cpbbz06r855gknxb8grn24gmgs4d9i";
+ type = "gem";
+ };
+ version = "5.0.4";
+ };
+ r2 = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0wk0p55zp3l96xy5ps28b33dn5z0jwsjl74bwfdn6z81pzjs5sfk";
+ type = "gem";
+ };
+ version = "0.2.7";
+ };
+ rack = {
+ groups = ["default" "development" "test"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0i5vs0dph9i5jn8dfc6aqd6njcafmb20rwqngrf759c9cvmyff16";
+ type = "gem";
+ };
+ version = "2.2.3";
+ };
+ rack-mini-profiler = {
+ dependencies = ["rack"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "05s7y56ayn56bn7y5ah3krm5d53vsj7apmcxlwc2qp7ik0xlypvq";
+ type = "gem";
+ };
+ version = "2.2.0";
+ };
+ rack-protection = {
+ dependencies = ["rack"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "159a4j4kragqh0z0z8vrpilpmaisnlz3n7kgiyf16bxkwlb3qlhz";
+ type = "gem";
+ };
+ version = "2.1.0";
+ };
+ rack-test = {
+ dependencies = ["rack"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0rh8h376mx71ci5yklnpqqn118z3bl67nnv5k801qaqn1zs62h8m";
+ type = "gem";
+ };
+ version = "1.1.0";
+ };
+ rails-dom-testing = {
+ dependencies = ["activesupport" "nokogiri"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1lfq2a7kp2x64dzzi5p4cjcbiv62vxh9lyqk2f0rqq3fkzrw8h5i";
+ type = "gem";
+ };
+ version = "2.0.3";
+ };
+ rails-html-sanitizer = {
+ dependencies = ["loofah"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1icpqmxbppl4ynzmn6dx7wdil5hhq6fz707m9ya6d86c7ys8sd4f";
+ type = "gem";
+ };
+ version = "1.3.0";
+ };
+ rails_failover = {
+ dependencies = ["activerecord" "concurrent-ruby" "railties"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0ibxn7lk6rqk7q76cd9ir3xnh19p2pqr9mzam46n3h37f12yyax5";
+ type = "gem";
+ };
+ version = "0.6.2";
+ };
+ rails_multisite = {
+ dependencies = ["activerecord" "railties"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0p7g9gkcmw030zfqlw3k933i40j31wf3jh4bj1niihzk7slha97y";
+ type = "gem";
+ };
+ version = "2.5.0";
+ };
+ railties = {
+ dependencies = ["actionpack" "activesupport" "method_source" "rake" "thor"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "05b79r0ms8jrs91zml1190qfxmnmks90g0sd820ks9msyr8xdp7j";
+ type = "gem";
+ };
+ version = "6.0.3.3";
+ };
+ rainbow = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0bb2fpjspydr6x0s8pn1pqkzmxszvkfapv0p4627mywl7ky4zkhk";
+ type = "gem";
+ };
+ version = "3.0.0";
+ };
+ raindrops = {
+ groups = ["default"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "rbx";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0zjja00mzgx2lddb7qrn14k7qrnwhf4bpmnlqj78m1pfxh7svync";
+ type = "gem";
+ };
+ version = "0.19.1";
+ };
+ rake = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0w6qza25bq1s825faaglkx1k6d59aiyjjk3yw3ip5sb463mhhai9";
+ type = "gem";
+ };
+ version = "13.0.1";
+ };
+ rb-fsevent = {
+ groups = ["development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1k9bsj7ni0g2fd7scyyy1sk9dy2pg9akniahab0iznvjmhn54h87";
+ type = "gem";
+ };
+ version = "0.10.4";
+ };
+ rb-inotify = {
+ dependencies = ["ffi"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1jm76h8f8hji38z3ggf4bzi8vps6p7sagxn3ab57qc0xyga64005";
+ type = "gem";
+ };
+ version = "0.10.1";
+ };
+ rbtrace = {
+ dependencies = ["ffi" "msgpack" "optimist"];
+ groups = ["default"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0s8prj0klfgpmpfcpdzbf149qrrsdxgnb6w6kkqc9gyars4vyaqn";
+ type = "gem";
+ };
+ version = "0.4.14";
+ };
+ rchardet = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1isj1b3ywgg2m1vdlnr41lpvpm3dbyarf1lla4dfibfmad9csfk9";
+ type = "gem";
+ };
+ version = "1.8.0";
+ };
+ redis = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "15x2sr6h094rjbvg8pkq6m3lcd5abpyx93aifvfdz3wv6x55xa48";
+ type = "gem";
+ };
+ version = "4.2.5";
+ };
+ redis-namespace = {
+ dependencies = ["redis"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "05i6s898z5w31z385cba1683pgg5nnmj4m686cbravg7j4pgbcgv";
+ type = "gem";
+ };
+ version = "1.8.0";
+ };
+ regexp_parser = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1racz3w9s4w0ls32bvjypfifk4a7qxngm2cv1rh16jyz0c1wjd70";
+ type = "gem";
+ };
+ version = "2.0.0";
+ };
+ request_store = {
+ dependencies = ["rack"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0cx74kispmnw3ljwb239j65a2j14n8jlsygy372hrsa8mxc71hxi";
+ type = "gem";
+ };
+ version = "1.5.0";
+ };
+ rexml = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1mkvkcw9fhpaizrhca0pdgjcrbns48rlz4g6lavl5gjjq3rk2sq3";
+ type = "gem";
+ };
+ version = "3.2.4";
+ };
+ rinku = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0zcdha17s1wzxyc5814j6319wqg33jbn58pg6wmxpws36476fq4b";
+ type = "gem";
+ };
+ version = "2.0.6";
+ };
+ rotp = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "11q7rkjx40yi6lpylgl2jkpy162mjw7mswrcgcax86vgpbpjx6i3";
+ type = "gem";
+ };
+ version = "6.2.0";
+ };
+ rqrcode = {
+ dependencies = ["chunky_png" "rqrcode_core"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "06lw8b6wfshxd61xw98xyp1a0zsz6av4nls2c9fwb7q59wb05sci";
+ type = "gem";
+ };
+ version = "1.1.2";
+ };
+ rqrcode_core = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "071jqmhk3hf0grsvi0jx5sl449pf82p40ls5b3likbq4q516zc0j";
+ type = "gem";
+ };
+ version = "0.1.2";
+ };
+ rspec = {
+ dependencies = ["rspec-core" "rspec-expectations" "rspec-mocks"];
+ groups = ["development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1dwai7jnwmdmd7ajbi2q0k0lx1dh88knv5wl7c34wjmf94yv8w5q";
+ type = "gem";
+ };
+ version = "3.10.0";
+ };
+ rspec-core = {
+ dependencies = ["rspec-support"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0n2rdv8f26yw8c6asymc0mgddyr5d2b5n6mfvpd3n6lnpf1jdyv2";
+ type = "gem";
+ };
+ version = "3.10.0";
+ };
+ rspec-expectations = {
+ dependencies = ["diff-lcs" "rspec-support"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0j37dvnvfbjwj8dqx27yfvz0frl7f2jc1abqg99h0ppriz9za6dc";
+ type = "gem";
+ };
+ version = "3.10.0";
+ };
+ rspec-html-matchers = {
+ dependencies = ["nokogiri" "rspec"];
+ groups = ["development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0883rqv77n2wawnk5lp3la48l7pckyz8l013qddngzmksi5p1v3f";
+ type = "gem";
+ };
+ version = "0.9.4";
+ };
+ rspec-mocks = {
+ dependencies = ["diff-lcs" "rspec-support"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1pz89y1522i6f8wzrg72ykmch3318ih87nlpl0y1ghsrs5hqymw3";
+ type = "gem";
+ };
+ version = "3.10.0";
+ };
+ rspec-rails = {
+ dependencies = ["actionpack" "activesupport" "railties" "rspec-core" "rspec-expectations" "rspec-mocks" "rspec-support"];
+ groups = ["development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0lzik01ziaskgpdpy8knffpw0fsy9151f5lfigyhb89wq4q45hfs";
+ type = "gem";
+ };
+ version = "4.0.1";
+ };
+ rspec-support = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0j0n28i6zci5j7gg370bdy87dy43hlwx6dw428d9kamf5a0i2klz";
+ type = "gem";
+ };
+ version = "3.10.0";
+ };
+ rswag-specs = {
+ dependencies = ["activesupport" "json-schema" "railties"];
+ groups = ["development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0lyp2m76p960bvgy4xcz0dilp4w5lq2cwh8md5z7cwxdg8qsbr83";
+ type = "gem";
+ };
+ version = "2.3.1";
+ };
+ rtlit = {
+ groups = ["assets"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0srfh7cl95srjiwbyc9pmn3w739zlvyj89hyj0bm7g92zrsd27qm";
+ type = "gem";
+ };
+ version = "0.0.5";
+ };
+ rubocop = {
+ dependencies = ["parallel" "parser" "rainbow" "regexp_parser" "rexml" "rubocop-ast" "ruby-progressbar" "unicode-display_width"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1kvzhzhzcdd5bqwjilb0fpp51sqjniww2b0g713n0cvhnlgchn2y";
+ type = "gem";
+ };
+ version = "1.4.2";
+ };
+ rubocop-ast = {
+ dependencies = ["parser"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0q0kdi89ad7dd1xmzrdf5ikk32bllzr68hf4x8fd7azcv5jnch2l";
+ type = "gem";
+ };
+ version = "1.2.0";
+ };
+ rubocop-discourse = {
+ dependencies = ["rubocop" "rubocop-rspec"];
+ groups = ["development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1z1h8spsjnsqz6c25n9ib1yimkwr7a76bas8w1k9c404hcqhlahv";
+ type = "gem";
+ };
+ version = "2.4.1";
+ };
+ rubocop-rspec = {
+ dependencies = ["rubocop" "rubocop-ast"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1gl7hdd9lq0si4gb510g33dbysmk3iydas2b0sbl5pwfkhv0k4g1";
+ type = "gem";
+ };
+ version = "2.0.0";
+ };
+ ruby-prof = {
+ groups = ["development"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1lm3wdxc6gjldkb5pdwwipapf84lgrvxck4h5kg8jdfd8arrpyis";
+ type = "gem";
+ };
+ version = "1.4.2";
+ };
+ ruby-progressbar = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1k77i0d4wsn23ggdd2msrcwfy0i376cglfqypkk2q77r2l3408zf";
+ type = "gem";
+ };
+ version = "1.10.1";
+ };
+ ruby-readability = {
+ dependencies = ["guess_html_encoding" "nokogiri"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "15ivhbry7hf82lww1bzcrwfyjymijfb3rb0wdd32g2z0942wdspa";
+ type = "gem";
+ };
+ version = "0.7.0";
+ };
+ ruby2_keywords = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "17pcc0wgvh3ikrkr7bm3nx0qhyiqwidd13ij0fa50k7gsbnr2p0l";
+ type = "gem";
+ };
+ version = "0.0.2";
+ };
+ rubyzip = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0590m2pr9i209pp5z4mx0nb1961ishdiqb28995hw1nln1d1b5ji";
+ type = "gem";
+ };
+ version = "2.3.0";
+ };
+ sanitize = {
+ dependencies = ["crass" "nokogiri" "nokogumbo"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "18m3zcf207gcrmghx288w3n2kpphc22lbmbc1wdx1nzcn8g2yddh";
+ type = "gem";
+ };
+ version = "5.2.1";
+ };
+ sassc = {
+ dependencies = ["ffi" "rake"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1sr4825rlwsrl7xrsm0sgalcpf5zgp4i56dbi3qxfa9lhs8r6zh4";
+ type = "gem";
+ };
+ version = "2.0.1";
+ };
+ sassc-rails = {
+ dependencies = ["railties" "sassc" "sprockets" "sprockets-rails" "tilt"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1d9djmwn36a5m8a83bpycs48g8kh1n2xkyvghn7dr6zwh4wdyksz";
+ type = "gem";
+ };
+ version = "2.1.2";
+ };
+ seed-fu = {
+ dependencies = ["activerecord" "activesupport"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0y7lzcshsq6i20qn1p8zczir4fivr6nbl1km91ns320vvh92v43d";
+ type = "gem";
+ };
+ version = "2.3.9";
+ };
+ shoulda-matchers = {
+ dependencies = ["activesupport"];
+ groups = ["development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1wd1bblxr4dfmrnh3j83kvfds6a7nak4ifq37ab0pg1kdi6iiw7l";
+ type = "gem";
+ };
+ version = "4.4.1";
+ };
+ sidekiq = {
+ dependencies = ["connection_pool" "rack" "redis"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0mjxrxppv08a1hwqi8gpg6n168cxqhp7c2r2jwc4rbz9j5k41vcw";
+ type = "gem";
+ };
+ version = "6.1.2";
+ };
+ simplecov = {
+ dependencies = ["docile" "simplecov-html" "simplecov_json_formatter"];
+ groups = ["test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1mm20dvd64w46l5k11il9z5sjgdpp0bknml76glcngvl2w03k3cb";
+ type = "gem";
+ };
+ version = "0.20.0";
+ };
+ simplecov-html = {
+ groups = ["default" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0yx01bxa8pbf9ip4hagqkp5m0mqfnwnw2xk8kjraiywz4lrss6jb";
+ type = "gem";
+ };
+ version = "0.12.3";
+ };
+ simplecov_json_formatter = {
+ groups = ["default" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0cl3j7p3b5q7sxsx1va63c8imc5x6g99xablz08qrmqhpi0d6g6j";
+ type = "gem";
+ };
+ version = "0.1.2";
+ };
+ sprockets = {
+ dependencies = ["concurrent-ruby" "rack"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "182jw5a0fbqah5w9jancvfmjbk88h8bxdbwnl4d3q809rpxdg8ay";
+ type = "gem";
+ };
+ version = "3.7.2";
+ };
+ sprockets-rails = {
+ dependencies = ["actionpack" "activesupport" "sprockets"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0mwmz36265646xqfyczgr1mhkm1hfxgxxvgdgr4xfcbf2g72p1k2";
+ type = "gem";
+ };
+ version = "3.2.2";
+ };
+ sshkey = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "03bkn55qsng484iqwz2lmm6rkimj01vsvhwk661s3lnmpkl65lbp";
+ type = "gem";
+ };
+ version = "2.0.0";
+ };
+ stackprof = {
+ groups = ["default"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "147rb66p3n062vc433afqhkd99iazvkrqnghxgh871r62yhha93f";
+ type = "gem";
+ };
+ version = "0.2.16";
+ };
+ test-prof = {
+ groups = ["test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1jfq8ylxpxanc3f0i6qb3nchawx9hj6qcqj6ccfyixrnvzswwjvi";
+ type = "gem";
+ };
+ version = "0.12.2";
+ };
+ thor = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1xbhkmyhlxwzshaqa7swy2bx6vd64mm0wrr8g3jywvxy7hg0cwkm";
+ type = "gem";
+ };
+ version = "1.0.1";
+ };
+ thread_safe = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0nmhcgq6cgz44srylra07bmaw99f5271l0dpsvl5f75m44l0gmwy";
+ type = "gem";
+ };
+ version = "0.3.6";
+ };
+ tilt = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0rn8z8hda4h41a64l0zhkiwz2vxw9b1nb70gl37h1dg2k874yrlv";
+ type = "gem";
+ };
+ version = "2.0.10";
+ };
+ tzinfo = {
+ dependencies = ["thread_safe"];
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0skr6ih9cr3pwp8l84f0z7fy3q9kiq8hw0sg3zqw0hpbbyj05743";
+ type = "gem";
+ };
+ version = "1.2.8";
+ };
+ uglifier = {
+ dependencies = ["execjs"];
+ groups = ["assets"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0wgh7bzy68vhv9v68061519dd8samcy8sazzz0w3k8kqpy3g4s5f";
+ type = "gem";
+ };
+ version = "4.2.0";
+ };
+ unf = {
+ dependencies = ["unf_ext"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0bh2cf73i2ffh4fcpdn9ir4mhq8zi50ik0zqa1braahzadx536a9";
+ type = "gem";
+ };
+ version = "0.1.4";
+ };
+ unf_ext = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0wc47r23h063l8ysws8sy24gzh74mks81cak3lkzlrw4qkqb3sg4";
+ type = "gem";
+ };
+ version = "0.0.7.7";
+ };
+ unicode-display_width = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "06i3id27s60141x6fdnjn5rar1cywdwy64ilc59cz937303q3mna";
+ type = "gem";
+ };
+ version = "1.7.0";
+ };
+ unicorn = {
+ dependencies = ["kgio" "raindrops"];
+ groups = ["default"];
+ platforms = [{
+ engine = "maglev";
+ } {
+ engine = "rbx";
+ } {
+ engine = "ruby";
+ }];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1qzdhbmab2w034wpdj5ippnyyvgqm8gpx9wbchb4zgs4i1mswzhv";
+ type = "gem";
+ };
+ version = "5.7.0";
+ };
+ uniform_notifier = {
+ groups = ["default" "development"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0vm4aix8jmv42s1x58m3lj3xwkbxyn9qn6lzhhig0d1j8fv6j30c";
+ type = "gem";
+ };
+ version = "1.13.0";
+ };
+ webmock = {
+ dependencies = ["addressable" "crack" "hashdiff"];
+ groups = ["test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "0wbdjagk2qpr76k3zw2gmkfp5aqlrc1a4qrpjv7sq1q39qbn8xax";
+ type = "gem";
+ };
+ version = "3.10.0";
+ };
+ webpush = {
+ dependencies = ["hkdf" "jwt"];
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1z9ma580q80czw46gi1bvsr2iwxr63aiyr7i9gilav6hbhg3sxv3";
+ type = "gem";
+ };
+ version = "1.1.0";
+ };
+ xorcist = {
+ groups = ["default"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1q7hr3qyn1hczv9fglqc2cbaax0fb37gjjr0y24x19mmp817csdn";
+ type = "gem";
+ };
+ version = "1.1.2";
+ };
+ yaml-lint = {
+ groups = ["development"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "1m9n4sg7i0334yac7dcrhnhv5rzvrccgnh687n9x77ba3awk4yx1";
+ type = "gem";
+ };
+ version = "0.0.10";
+ };
+ zeitwerk = {
+ groups = ["default" "development" "test"];
+ platforms = [];
+ source = {
+ remotes = ["https://rubygems.org"];
+ sha256 = "12n0hiawqayzchi0yga5n19hi63b2snd49fv3n23n2i4pp05jzrp";
+ type = "gem";
+ };
+ version = "2.4.1";
+ };
+}
diff --git a/pkgs/servers/web-apps/discourse/unicorn_logging_and_timeout.patch b/pkgs/servers/web-apps/discourse/unicorn_logging_and_timeout.patch
new file mode 100644
index 000000000000..1dbfed679192
--- /dev/null
+++ b/pkgs/servers/web-apps/discourse/unicorn_logging_and_timeout.patch
@@ -0,0 +1,25 @@
+diff --git a/config/unicorn.conf.rb b/config/unicorn.conf.rb
+index 373e235b3f..57d4d7a55b 100644
+--- a/config/unicorn.conf.rb
++++ b/config/unicorn.conf.rb
+@@ -27,18 +27,10 @@ pid (ENV["UNICORN_PID_PATH"] || "#{discourse_path}/tmp/pids/unicorn.pid")
+
+ if ENV["RAILS_ENV"] == "development" || !ENV["RAILS_ENV"]
+ logger Logger.new($stdout)
+- # we want a longer timeout in dev cause first request can be really slow
+- timeout (ENV["UNICORN_TIMEOUT"] && ENV["UNICORN_TIMEOUT"].to_i || 60)
+-else
+- # By default, the Unicorn logger will write to stderr.
+- # Additionally, some applications/frameworks log to stderr or stdout,
+- # so prevent them from going to /dev/null when daemonized here:
+- stderr_path "#{discourse_path}/log/unicorn.stderr.log"
+- stdout_path "#{discourse_path}/log/unicorn.stdout.log"
+- # nuke workers after 30 seconds instead of 60 seconds (the default)
+- timeout 30
+ end
+
++timeout (ENV["UNICORN_TIMEOUT"] && ENV["UNICORN_TIMEOUT"].to_i || 60)
++
+ # important for Ruby 2.0
+ preload_app true
+
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index 490fac7cfe36..cf9a3349dd59 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -2220,6 +2220,10 @@ in
discount = callPackage ../tools/text/discount { };
+ discourse = callPackage ../servers/web-apps/discourse {
+ ruby = ruby_2_7;
+ };
+
discocss = callPackage ../tools/misc/discocss { };
disfetch = callPackage ../tools/misc/disfetch { };
From 1d3715d375f50d3bcd8363ca02239888b3f46a05 Mon Sep 17 00:00:00 2001
From: talyz
Date: Thu, 18 Mar 2021 22:15:28 +0100
Subject: [PATCH 4/9] discourse-mail-receiver: Init at 4.0.7
---
.../discourse/mail_receiver/default.nix | 39 +++++++++++++++++++
pkgs/top-level/all-packages.nix | 4 ++
2 files changed, 43 insertions(+)
create mode 100644 pkgs/servers/web-apps/discourse/mail_receiver/default.nix
diff --git a/pkgs/servers/web-apps/discourse/mail_receiver/default.nix b/pkgs/servers/web-apps/discourse/mail_receiver/default.nix
new file mode 100644
index 000000000000..c1a3a2df1064
--- /dev/null
+++ b/pkgs/servers/web-apps/discourse/mail_receiver/default.nix
@@ -0,0 +1,39 @@
+{ stdenv, lib, fetchFromGitHub, ruby, makeWrapper, replace }:
+
+stdenv.mkDerivation rec {
+ pname = "discourse-mail-receiver";
+ version = "4.0.7";
+
+ src = fetchFromGitHub {
+ owner = "discourse";
+ repo = "mail-receiver";
+ rev = "v${version}";
+ sha256 = "0grifm5qyqazq63va3w26xjqnxwmfixhx0fx0zy7kd39378wwa6i";
+ };
+
+ nativeBuildInputs = [ replace ];
+ buildInputs = [ ruby makeWrapper ];
+
+ dontBuild = true;
+
+ installPhase = ''
+ mkdir -p $out/bin
+
+ replace-literal -f -r -e /etc/postfix /run/discourse-mail-receiver .
+
+ cp -r receive-mail discourse-smtp-fast-rejection $out/bin/
+ cp -r lib $out/
+
+ wrapProgram $out/bin/receive-mail --set RUBYLIB $out/lib
+ wrapProgram $out/bin/discourse-smtp-fast-rejection --set RUBYLIB $out/lib
+ '';
+
+ meta = with lib; {
+ homepage = "https://www.discourse.org/";
+ platforms = platforms.linux;
+ maintainers = with maintainers; [ talyz ];
+ license = licenses.mit;
+ description = "A helper program which receives incoming mail for Discourse";
+ };
+
+}
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index cf9a3349dd59..6ac81ec29ae7 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -2224,6 +2224,10 @@ in
ruby = ruby_2_7;
};
+ discourse-mail-receiver = callPackage ../servers/web-apps/discourse/mail_receiver {
+ ruby = ruby_2_7;
+ };
+
discocss = callPackage ../tools/misc/discocss { };
disfetch = callPackage ../tools/misc/disfetch { };
From 8dddb70bb97ff76c291d582295fba0791ed1c82a Mon Sep 17 00:00:00 2001
From: talyz
Date: Sun, 4 Apr 2021 13:42:18 +0200
Subject: [PATCH 5/9] nixos/discourse: Init
---
nixos/modules/module-list.nix | 1 +
nixos/modules/services/web-apps/discourse.nix | 1032 +++++++++++++++++
2 files changed, 1033 insertions(+)
create mode 100644 nixos/modules/services/web-apps/discourse.nix
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index ca7898687b8b..aa118b66367b 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -890,6 +890,7 @@
./services/web-apps/bookstack.nix
./services/web-apps/convos.nix
./services/web-apps/cryptpad.nix
+ ./services/web-apps/discourse.nix
./services/web-apps/documize.nix
./services/web-apps/dokuwiki.nix
./services/web-apps/engelsystem.nix
diff --git a/nixos/modules/services/web-apps/discourse.nix b/nixos/modules/services/web-apps/discourse.nix
new file mode 100644
index 000000000000..6f4c50006d3e
--- /dev/null
+++ b/nixos/modules/services/web-apps/discourse.nix
@@ -0,0 +1,1032 @@
+{ config, options, lib, pkgs, utils, ... }:
+
+let
+ json = pkgs.formats.json {};
+
+ cfg = config.services.discourse;
+
+ postgresqlPackage = if config.services.postgresql.enable then
+ config.services.postgresql.package
+ else
+ pkgs.postgresql;
+
+ # We only want to create a database if we're actually going to connect to it.
+ databaseActuallyCreateLocally = cfg.database.createLocally && cfg.database.host == null;
+
+ tlsEnabled = (cfg.enableACME
+ || cfg.sslCertificate != null
+ || cfg.sslCertificateKey != null);
+in
+{
+ options = {
+ services.discourse = {
+ enable = lib.mkEnableOption "Discourse, an open source discussion platform";
+
+ package = lib.mkOption {
+ type = lib.types.package;
+ default = pkgs.discourse;
+ defaultText = "pkgs.discourse";
+ description = ''
+ The discourse package to use.
+ '';
+ };
+
+ hostname = lib.mkOption {
+ type = lib.types.str;
+ default = if config.networking.domain != null then
+ config.networking.fqdn
+ else
+ config.networking.hostName;
+ defaultText = "config.networking.fqdn";
+ example = "discourse.example.com";
+ description = ''
+ The hostname to serve Discourse on.
+ '';
+ };
+
+ secretKeyBaseFile = lib.mkOption {
+ type = with lib.types; nullOr path;
+ default = null;
+ example = "/run/keys/secret_key_base";
+ description = ''
+ The path to a file containing the
+ secret_key_base secret.
+
+ Discourse uses secret_key_base to encrypt
+ the cookie store, which contains session data, and to digest
+ user auth tokens.
+
+ Needs to be a 64 byte long string of hexadecimal
+ characters. You can generate one by running
+
+
+ $ openssl rand -hex 64 >/path/to/secret_key_base_file
+
+
+ This should be a string, not a nix path, since nix paths are
+ copied into the world-readable nix store.
+ '';
+ };
+
+ sslCertificate = lib.mkOption {
+ type = with lib.types; nullOr path;
+ default = null;
+ example = "/run/keys/ssl.cert";
+ description = ''
+ The path to the server SSL certificate. Set this to enable
+ SSL.
+ '';
+ };
+
+ sslCertificateKey = lib.mkOption {
+ type = with lib.types; nullOr path;
+ default = null;
+ example = "/run/keys/ssl.key";
+ description = ''
+ The path to the server SSL certificate key. Set this to
+ enable SSL.
+ '';
+ };
+
+ enableACME = lib.mkOption {
+ type = lib.types.bool;
+ default = cfg.sslCertificate == null && cfg.sslCertificateKey == null;
+ defaultText = "true, unless services.discourse.sslCertificate and services.discourse.sslCertificateKey are set.";
+ description = ''
+ Whether an ACME certificate should be used to secure
+ connections to the server.
+ '';
+ };
+
+ backendSettings = lib.mkOption {
+ type = with lib.types; attrsOf (nullOr (oneOf [ str int bool float ]));
+ default = {};
+ example = lib.literalExample ''
+ {
+ max_reqs_per_ip_per_minute = 300;
+ max_reqs_per_ip_per_10_seconds = 60;
+ max_asset_reqs_per_ip_per_10_seconds = 250;
+ max_reqs_per_ip_mode = "warn+block";
+ };
+ '';
+ description = ''
+ Additional settings to put in the
+ discourse.conf file.
+
+ Look in the
+ discourse_defaults.conf
+ file in the upstream distribution to find available options.
+
+ Setting an option to null means
+ define variable, but leave right-hand side
+ empty
.
+ '';
+ };
+
+ siteSettings = lib.mkOption {
+ type = json.type;
+ default = {};
+ example = lib.literalExample ''
+ {
+ required = {
+ title = "My Cats";
+ site_description = "Discuss My Cats (and be nice plz)";
+ };
+ login = {
+ enable_github_logins = true;
+ github_client_id = "a2f6dfe838cb3206ce20";
+ github_client_secret._secret = /run/keys/discourse_github_client_secret;
+ };
+ };
+ '';
+ description = ''
+ Discourse site settings. These are the settings that can be
+ changed from the UI. This only defines their default values:
+ they can still be overridden from the UI.
+
+ Available settings can be found by looking in the
+ site_settings.yml
+ file of the upstream distribution. To find a setting's path,
+ you only need to care about the first two levels; i.e. its
+ category and name. See the example.
+
+ Settings containing secret data should be set to an
+ attribute set containing the attribute
+ _secret - a string pointing to a file
+ containing the value the option should be set to. See the
+ example to get a better picture of this: in the resulting
+ config/nixos_site_settings.json file,
+ the login.github_client_secret key will
+ be set to the contents of the
+ /run/keys/discourse_github_client_secret
+ file.
+ '';
+ };
+
+ admin = {
+ email = lib.mkOption {
+ type = lib.types.str;
+ example = "admin@example.com";
+ description = ''
+ The admin user email address.
+ '';
+ };
+
+ username = lib.mkOption {
+ type = lib.types.str;
+ example = "admin";
+ description = ''
+ The admin user username.
+ '';
+ };
+
+ fullName = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ The admin user's full name.
+ '';
+ };
+
+ passwordFile = lib.mkOption {
+ type = lib.types.path;
+ description = ''
+ A path to a file containing the admin user's password.
+
+ This should be a string, not a nix path, since nix paths are
+ copied into the world-readable nix store.
+ '';
+ };
+ };
+
+ nginx.enable = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ Whether an nginx virtual host should be
+ set up to serve Discourse. Only disable if you're planning
+ to use a different web server, which is not recommended.
+ '';
+ };
+
+ database = {
+ pool = lib.mkOption {
+ type = lib.types.int;
+ default = 8;
+ description = ''
+ Database connection pool size.
+ '';
+ };
+
+ host = lib.mkOption {
+ type = with lib.types; nullOr str;
+ default = null;
+ description = ''
+ Discourse database hostname. null means prefer
+ local unix socket connection
.
+ '';
+ };
+
+ passwordFile = lib.mkOption {
+ type = with lib.types; nullOr path;
+ default = null;
+ description = ''
+ File containing the Discourse database user password.
+
+ This should be a string, not a nix path, since nix paths are
+ copied into the world-readable nix store.
+ '';
+ };
+
+ createLocally = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ Whether a database should be automatically created on the
+ local host. Set this to false if you plan
+ on provisioning a local database yourself. This has no effect
+ if services.discourse.database.host is customized.
+ '';
+ };
+
+ name = lib.mkOption {
+ type = lib.types.str;
+ default = "discourse";
+ description = ''
+ Discourse database name.
+ '';
+ };
+
+ username = lib.mkOption {
+ type = lib.types.str;
+ default = "discourse";
+ description = ''
+ Discourse database user.
+ '';
+ };
+ };
+
+ redis = {
+ host = lib.mkOption {
+ type = lib.types.str;
+ default = "localhost";
+ description = ''
+ Redis server hostname.
+ '';
+ };
+
+ passwordFile = lib.mkOption {
+ type = with lib.types; nullOr path;
+ default = null;
+ description = ''
+ File containing the Redis password.
+
+ This should be a string, not a nix path, since nix paths are
+ copied into the world-readable nix store.
+ '';
+ };
+
+ dbNumber = lib.mkOption {
+ type = lib.types.int;
+ default = 0;
+ description = ''
+ Redis database number.
+ '';
+ };
+
+ useSSL = lib.mkOption {
+ type = lib.types.bool;
+ default = cfg.redis.host != "localhost";
+ description = ''
+ Connect to Redis with SSL.
+ '';
+ };
+ };
+
+ mail = {
+ notificationEmailAddress = lib.mkOption {
+ type = lib.types.str;
+ default = "${if cfg.mail.incoming.enable then "notifications" else "noreply"}@${cfg.hostname}";
+ defaultText = ''
+ "notifications@`config.services.discourse.hostname`" if
+ config.services.discourse.mail.incoming.enable is "true",
+ otherwise "noreply`config.services.discourse.hostname`"
+ '';
+ description = ''
+ The from: email address used when
+ sending all essential system emails. The domain specified
+ here must have SPF, DKIM and reverse PTR records set
+ correctly for email to arrive.
+ '';
+ };
+
+ contactEmailAddress = lib.mkOption {
+ type = lib.types.str;
+ default = "";
+ description = ''
+ Email address of key contact responsible for this
+ site. Used for critical notifications, as well as on the
+ /about contact form for urgent matters.
+ '';
+ };
+
+ outgoing = {
+ serverAddress = lib.mkOption {
+ type = lib.types.str;
+ default = "localhost";
+ description = ''
+ The address of the SMTP server Discourse should use to
+ send email.
+ '';
+ };
+
+ port = lib.mkOption {
+ type = lib.types.int;
+ default = 25;
+ description = ''
+ The port of the SMTP server Discourse should use to
+ send email.
+ '';
+ };
+
+ username = lib.mkOption {
+ type = with lib.types; nullOr str;
+ default = null;
+ description = ''
+ The username of the SMTP server.
+ '';
+ };
+
+ passwordFile = lib.mkOption {
+ type = lib.types.nullOr lib.types.path;
+ default = null;
+ description = ''
+ A file containing the password of the SMTP server account.
+
+ This should be a string, not a nix path, since nix paths
+ are copied into the world-readable nix store.
+ '';
+ };
+
+ domain = lib.mkOption {
+ type = lib.types.str;
+ default = cfg.hostname;
+ description = ''
+ HELO domain to use for outgoing mail.
+ '';
+ };
+
+ authentication = lib.mkOption {
+ type = with lib.types; nullOr (enum ["plain" "login" "cram_md5"]);
+ default = null;
+ description = ''
+ Authentication type to use, see http://api.rubyonrails.org/classes/ActionMailer/Base.html
+ '';
+ };
+
+ enableStartTLSAuto = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ Whether to try to use StartTLS.
+ '';
+ };
+
+ opensslVerifyMode = lib.mkOption {
+ type = lib.types.str;
+ default = "peer";
+ description = ''
+ How OpenSSL checks the certificate, see http://api.rubyonrails.org/classes/ActionMailer/Base.html
+ '';
+ };
+ };
+
+ incoming = {
+ enable = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Whether to set up Postfix to receive incoming mail.
+ '';
+ };
+
+ replyEmailAddress = lib.mkOption {
+ type = lib.types.str;
+ default = "%{reply_key}@${cfg.hostname}";
+ defaultText = "%{reply_key}@`config.services.discourse.hostname`";
+ description = ''
+ Template for reply by email incoming email address, for
+ example: %{reply_key}@reply.example.com or
+ replies+%{reply_key}@example.com
+ '';
+ };
+
+ mailReceiverPackage = lib.mkOption {
+ type = lib.types.package;
+ default = pkgs.discourse-mail-receiver;
+ defaultText = "pkgs.discourse-mail-receiver";
+ description = ''
+ The discourse-mail-receiver package to use.
+ '';
+ };
+
+ apiKeyFile = lib.mkOption {
+ type = lib.types.nullOr lib.types.path;
+ default = null;
+ description = ''
+ A file containing the Discourse API key used to add
+ posts and messages from mail. If left at its default
+ value null , one will be automatically
+ generated.
+
+ This should be a string, not a nix path, since nix paths
+ are copied into the world-readable nix store.
+ '';
+ };
+ };
+ };
+
+ plugins = lib.mkOption {
+ type = lib.types.listOf lib.types.package;
+ default = [];
+ example = ''
+ [
+ (pkgs.fetchFromGitHub {
+ owner = "discourse";
+ repo = "discourse-spoiler-alert";
+ rev = "e200cfa571d252cab63f3d30d619b370986e4cee";
+ sha256 = "0ya69ix5g77wz4c9x9gmng6l25ghb5xxlx3icr6jam16q14dzc33";
+ })
+ ];
+ '';
+ description = ''
+ Discourse plugins to install as a
+ list of derivations. As long as a plugin supports the
+ standard install method, packaging it should only require
+ fetching its source with an appropriate fetcher.
+ '';
+ };
+
+ sidekiqProcesses = lib.mkOption {
+ type = lib.types.int;
+ default = 1;
+ description = ''
+ How many Sidekiq processes should be spawned.
+ '';
+ };
+
+ unicornTimeout = lib.mkOption {
+ type = lib.types.int;
+ default = 30;
+ description = ''
+ Time in seconds before a request to Unicorn times out.
+
+ This can be raised if the system Discourse is running on is
+ too slow to handle many requests within 30 seconds.
+ '';
+ };
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ assertions = [
+ {
+ assertion = (cfg.database.host != null) -> (cfg.database.passwordFile != null);
+ message = "When services.gitlab.database.host is customized, services.discourse.database.passwordFile must be set!";
+ }
+ {
+ assertion = cfg.hostname != "";
+ message = "Could not automatically determine hostname, set service.discourse.hostname manually.";
+ }
+ ];
+
+
+ # Default config values are from `config/discourse_defaults.conf`
+ # upstream.
+ services.discourse.backendSettings = lib.mapAttrs (_: lib.mkDefault) {
+ db_pool = cfg.database.pool;
+ db_timeout = 5000;
+ db_connect_timeout = 5;
+ db_socket = null;
+ db_host = cfg.database.host;
+ db_backup_host = null;
+ db_port = null;
+ db_backup_port = 5432;
+ db_name = cfg.database.name;
+ db_username = if databaseActuallyCreateLocally then "discourse" else cfg.database.username;
+ db_password = cfg.database.passwordFile;
+ db_prepared_statements = false;
+ db_replica_host = null;
+ db_replica_port = null;
+ db_advisory_locks = true;
+
+ inherit (cfg) hostname;
+ backup_hostname = null;
+
+ smtp_address = cfg.mail.outgoing.serverAddress;
+ smtp_port = cfg.mail.outgoing.port;
+ smtp_domain = cfg.mail.outgoing.domain;
+ smtp_user_name = cfg.mail.outgoing.username;
+ smtp_password = cfg.mail.outgoing.passwordFile;
+ smtp_authentication = cfg.mail.outgoing.authentication;
+ smtp_enable_start_tls = cfg.mail.outgoing.enableStartTLSAuto;
+ smtp_openssl_verify_mode = cfg.mail.outgoing.opensslVerifyMode;
+
+ load_mini_profiler = true;
+ mini_profiler_snapshots_period = 0;
+ mini_profiler_snapshots_transport_url = null;
+ mini_profiler_snapshots_transport_auth_key = null;
+
+ cdn_url = null;
+ cdn_origin_hostname = null;
+ developer_emails = null;
+
+ redis_host = cfg.redis.host;
+ redis_port = 6379;
+ redis_slave_host = null;
+ redis_slave_port = 6379;
+ redis_db = cfg.redis.dbNumber;
+ redis_password = cfg.redis.passwordFile;
+ redis_skip_client_commands = false;
+ redis_use_ssl = cfg.redis.useSSL;
+
+ message_bus_redis_enabled = false;
+ message_bus_redis_host = "localhost";
+ message_bus_redis_port = 6379;
+ message_bus_redis_slave_host = null;
+ message_bus_redis_slave_port = 6379;
+ message_bus_redis_db = 0;
+ message_bus_redis_password = null;
+ message_bus_redis_skip_client_commands = false;
+
+ enable_cors = false;
+ cors_origin = "";
+ serve_static_assets = false;
+ sidekiq_workers = 5;
+ rtl_css = false;
+ connection_reaper_age = 30;
+ connection_reaper_interval = 30;
+ relative_url_root = null;
+ message_bus_max_backlog_size = 100;
+ secret_key_base = cfg.secretKeyBaseFile;
+ fallback_assets_path = null;
+
+ s3_bucket = null;
+ s3_region = null;
+ s3_access_key_id = null;
+ s3_secret_access_key = null;
+ s3_use_iam_profile = null;
+ s3_cdn_url = null;
+ s3_endpoint = null;
+ s3_http_continue_timeout = null;
+ s3_install_cors_rule = null;
+
+ max_user_api_reqs_per_minute = 20;
+ max_user_api_reqs_per_day = 2880;
+ max_admin_api_reqs_per_key_per_minute = 60;
+ max_reqs_per_ip_per_minute = 200;
+ max_reqs_per_ip_per_10_seconds = 50;
+ max_asset_reqs_per_ip_per_10_seconds = 200;
+ max_reqs_per_ip_mode = "block";
+ max_reqs_rate_limit_on_private = false;
+ force_anonymous_min_queue_seconds = 1;
+ force_anonymous_min_per_10_seconds = 3;
+ background_requests_max_queue_length = 0.5;
+ reject_message_bus_queue_seconds = 0.1;
+ disable_search_queue_threshold = 1;
+ max_old_rebakes_per_15_minutes = 300;
+ max_logster_logs = 1000;
+ refresh_maxmind_db_during_precompile_days = 2;
+ maxmind_backup_path = null;
+ maxmind_license_key = null;
+ enable_performance_http_headers = false;
+ enable_js_error_reporting = true;
+ mini_scheduler_workers = 5;
+ compress_anon_cache = false;
+ anon_cache_store_threshold = 2;
+ allowed_theme_repos = null;
+ enable_email_sync_demon = false;
+ max_digests_enqueued_per_30_mins_per_site = 10000;
+ };
+
+ services.redis.enable = lib.mkDefault (cfg.redis.host == "localhost");
+
+ services.postgresql = lib.mkIf databaseActuallyCreateLocally {
+ enable = true;
+ ensureUsers = [{ name = "discourse"; }];
+ };
+
+ # The postgresql module doesn't currently support concepts like
+ # objects owners and extensions; for now we tack on what's needed
+ # here.
+ systemd.services.discourse-postgresql =
+ let
+ pgsql = config.services.postgresql;
+ in
+ lib.mkIf databaseActuallyCreateLocally {
+ after = [ "postgresql.service" ];
+ bindsTo = [ "postgresql.service" ];
+ wantedBy = [ "discourse.service" ];
+ partOf = [ "discourse.service" ];
+ path = [
+ pgsql.package
+ ];
+ script = ''
+ set -o errexit -o pipefail -o nounset -o errtrace
+ shopt -s inherit_errexit
+
+ psql -tAc "SELECT 1 FROM pg_database WHERE datname = 'discourse'" | grep -q 1 || psql -tAc 'CREATE DATABASE "discourse" OWNER "discourse"'
+ psql '${cfg.database.name}' -tAc "CREATE EXTENSION IF NOT EXISTS pg_trgm"
+ psql '${cfg.database.name}' -tAc "CREATE EXTENSION IF NOT EXISTS hstore"
+ '';
+
+ serviceConfig = {
+ User = pgsql.superUser;
+ Type = "oneshot";
+ RemainAfterExit = true;
+ };
+ };
+
+ systemd.services.discourse = {
+ wantedBy = [ "multi-user.target" ];
+ after = [
+ "redis.service"
+ "postgresql.service"
+ "discourse-postgresql.service"
+ ];
+ bindsTo = [
+ "redis.service"
+ ] ++ lib.optionals (cfg.database.host == null) [
+ "postgresql.service"
+ "discourse-postgresql.service"
+ ];
+ path = cfg.package.runtimeDeps ++ [
+ postgresqlPackage
+ pkgs.replace
+ cfg.package.rake
+ ];
+ environment = cfg.package.runtimeEnv // {
+ UNICORN_TIMEOUT = builtins.toString cfg.unicornTimeout;
+ UNICORN_SIDEKIQS = builtins.toString cfg.sidekiqProcesses;
+ };
+
+ preStart =
+ let
+ discourseKeyValue = lib.generators.toKeyValue {
+ mkKeyValue = lib.flip lib.generators.mkKeyValueDefault " = " {
+ mkValueString = v: with builtins;
+ if isInt v then toString v
+ else if isString v then ''"${v}"''
+ else if true == v then "true"
+ else if false == v then "false"
+ else if null == v then ""
+ else if isFloat v then lib.strings.floatToString v
+ else throw "unsupported type ${typeOf v}: ${(lib.generators.toPretty {}) v}";
+ };
+ };
+
+ discourseConf = pkgs.writeText "discourse.conf" (discourseKeyValue cfg.backendSettings);
+
+ mkSecretReplacement = file:
+ lib.optionalString (file != null) ''
+ (
+ password=$(<'${file}')
+ replace-literal -fe '${file}' "$password" /run/discourse/config/discourse.conf
+ )
+ '';
+ in ''
+ set -o errexit -o pipefail -o nounset -o errtrace
+ shopt -s inherit_errexit
+
+ umask u=rwx,g=rx,o=
+
+ cp -r ${cfg.package}/share/discourse/config.dist/* /run/discourse/config/
+ cp -r ${cfg.package}/share/discourse/public.dist/* /run/discourse/public/
+ cp -r ${cfg.package}/share/discourse/plugins.dist/* /run/discourse/plugins/
+ ${lib.concatMapStrings (p: "ln -sf ${p} /run/discourse/plugins/") cfg.plugins}
+ ln -sf /var/lib/discourse/uploads /run/discourse/public/uploads
+ ln -sf /var/lib/discourse/backups /run/discourse/public/backups
+
+ (
+ umask u=rwx,g=,o=
+
+ ${utils.genJqSecretsReplacementSnippet
+ cfg.siteSettings
+ "/run/discourse/config/nixos_site_settings.json"
+ }
+ install -T -m 0400 -o discourse ${discourseConf} /run/discourse/config/discourse.conf
+ ${mkSecretReplacement cfg.database.passwordFile}
+ ${mkSecretReplacement cfg.mail.outgoing.passwordFile}
+ ${mkSecretReplacement cfg.redis.passwordFile}
+ ${mkSecretReplacement cfg.secretKeyBaseFile}
+ )
+
+ discourse-rake db:migrate >>/var/log/discourse/db_migration.log
+ chmod -R u+w /run/discourse/tmp/
+
+ export ADMIN_EMAIL="${cfg.admin.email}"
+ export ADMIN_NAME="${cfg.admin.fullName}"
+ export ADMIN_USERNAME="${cfg.admin.username}"
+ export ADMIN_PASSWORD="$(<${cfg.admin.passwordFile})"
+ discourse-rake admin:create_noninteractively
+
+ discourse-rake themes:update
+ discourse-rake uploads:regenerate_missing_optimized
+ '';
+
+ serviceConfig = {
+ Type = "simple";
+ User = "discourse";
+ Group = "discourse";
+ RuntimeDirectory = map (p: "discourse/" + p) [
+ "config"
+ "home"
+ "tmp"
+ "assets/javascripts/plugins"
+ "public"
+ "plugins"
+ "sockets"
+ ];
+ RuntimeDirectoryMode = 0750;
+ StateDirectory = map (p: "discourse/" + p) [
+ "uploads"
+ "backups"
+ ];
+ StateDirectoryMode = 0750;
+ LogsDirectory = "discourse";
+ TimeoutSec = "infinity";
+ Restart = "on-failure";
+ WorkingDirectory = "${cfg.package}/share/discourse";
+
+ RemoveIPC = true;
+ PrivateTmp = true;
+ NoNewPrivileges = true;
+ RestrictSUIDSGID = true;
+ ProtectSystem = "strict";
+ ProtectHome = "read-only";
+
+ ExecStart = "${cfg.package.rubyEnv}/bin/bundle exec config/unicorn_launcher -E production -c config/unicorn.conf.rb";
+ };
+ };
+
+ services.nginx = lib.mkIf cfg.nginx.enable {
+ enable = true;
+ additionalModules = [ pkgs.nginxModules.brotli ];
+
+ recommendedTlsSettings = true;
+ recommendedOptimisation = true;
+ recommendedGzipSettings = true;
+ recommendedProxySettings = true;
+
+ upstreams.discourse.servers."unix:/run/discourse/sockets/unicorn.sock" = {};
+
+ appendHttpConfig = ''
+ # inactive means we keep stuff around for 1440m minutes regardless of last access (1 week)
+ # levels means it is a 2 deep heirarchy cause we can have lots of files
+ # max_size limits the size of the cache
+ proxy_cache_path /var/cache/nginx inactive=1440m levels=1:2 keys_zone=discourse:10m max_size=600m;
+
+ # see: https://meta.discourse.org/t/x/74060
+ proxy_buffer_size 8k;
+ '';
+
+ virtualHosts.${cfg.hostname} = {
+ inherit (cfg) sslCertificate sslCertificateKey enableACME;
+ forceSSL = lib.mkDefault tlsEnabled;
+
+ root = "/run/discourse/public";
+
+ locations =
+ let
+ proxy = { extraConfig ? "" }: {
+ proxyPass = "http://discourse";
+ extraConfig = extraConfig + ''
+ proxy_set_header X-Request-Start "t=''${msec}";
+ '';
+ };
+ cache = time: ''
+ expires ${time};
+ add_header Cache-Control public,immutable;
+ '';
+ cache_1y = cache "1y";
+ cache_1d = cache "1d";
+ in
+ {
+ "/".tryFiles = "$uri @discourse";
+ "@discourse" = proxy {};
+ "^~ /backups/".extraConfig = ''
+ internal;
+ '';
+ "/favicon.ico" = {
+ return = "204";
+ extraConfig = ''
+ access_log off;
+ log_not_found off;
+ '';
+ };
+ "~ ^/uploads/short-url/" = proxy {};
+ "~ ^/secure-media-uploads/" = proxy {};
+ "~* (fonts|assets|plugins|uploads)/.*\.(eot|ttf|woff|woff2|ico|otf)$".extraConfig = cache_1y + ''
+ add_header Access-Control-Allow-Origin *;
+ '';
+ "/srv/status" = proxy {
+ extraConfig = ''
+ access_log off;
+ log_not_found off;
+ '';
+ };
+ "~ ^/javascripts/".extraConfig = cache_1d;
+ "~ ^/assets/(?.+)$".extraConfig = cache_1y + ''
+ # asset pipeline enables this
+ brotli_static on;
+ gzip_static on;
+ '';
+ "~ ^/plugins/".extraConfig = cache_1y;
+ "~ /images/emoji/".extraConfig = cache_1y;
+ "~ ^/uploads/" = proxy {
+ extraConfig = cache_1y + ''
+ proxy_set_header X-Sendfile-Type X-Accel-Redirect;
+ proxy_set_header X-Accel-Mapping /run/discourse/public/=/downloads/;
+
+ # custom CSS
+ location ~ /stylesheet-cache/ {
+ try_files $uri =404;
+ }
+ # this allows us to bypass rails
+ location ~* \.(gif|png|jpg|jpeg|bmp|tif|tiff|ico|webp)$ {
+ try_files $uri =404;
+ }
+ # SVG needs an extra header attached
+ location ~* \.(svg)$ {
+ }
+ # thumbnails & optimized images
+ location ~ /_?optimized/ {
+ try_files $uri =404;
+ }
+ '';
+ };
+ "~ ^/admin/backups/" = proxy {
+ extraConfig = ''
+ proxy_set_header X-Sendfile-Type X-Accel-Redirect;
+ proxy_set_header X-Accel-Mapping /run/discourse/public/=/downloads/;
+ '';
+ };
+ "~ ^/(svg-sprite/|letter_avatar/|letter_avatar_proxy/|user_avatar|highlight-js|stylesheets|theme-javascripts|favicon/proxied|service-worker)" = proxy {
+ extraConfig = ''
+ # if Set-Cookie is in the response nothing gets cached
+ # this is double bad cause we are not passing last modified in
+ proxy_ignore_headers "Set-Cookie";
+ proxy_hide_header "Set-Cookie";
+ proxy_hide_header "X-Discourse-Username";
+ proxy_hide_header "X-Runtime";
+
+ # note x-accel-redirect can not be used with proxy_cache
+ proxy_cache discourse;
+ proxy_cache_key "$scheme,$host,$request_uri";
+ proxy_cache_valid 200 301 302 7d;
+ proxy_cache_valid any 1m;
+ '';
+ };
+ "/message-bus/" = proxy {
+ extraConfig = ''
+ proxy_http_version 1.1;
+ proxy_buffering off;
+ '';
+ };
+ "/downloads/".extraConfig = ''
+ internal;
+ alias /run/discourse/public/;
+ '';
+ };
+ };
+ };
+
+ systemd.services.discourse-mail-receiver-setup = lib.mkIf cfg.mail.incoming.enable (
+ let
+ mail-receiver-environment = {
+ MAIL_DOMAIN = cfg.hostname;
+ DISCOURSE_BASE_URL = "http${lib.optionalString tlsEnabled "s"}://${cfg.hostname}";
+ DISCOURSE_API_KEY = "@api-key@";
+ DISCOURSE_API_USERNAME = "system";
+ };
+ mail-receiver-json = json.generate "mail-receiver.json" mail-receiver-environment;
+ in
+ {
+ before = [ "postfix.service" ];
+ after = [ "discourse.service" ];
+ wantedBy = [ "discourse.service" ];
+ partOf = [ "discourse.service" ];
+ path = [
+ cfg.package.rake
+ pkgs.jq
+ ];
+ preStart = lib.optionalString (cfg.mail.incoming.apiKeyFile == null) ''
+ set -o errexit -o pipefail -o nounset -o errtrace
+ shopt -s inherit_errexit
+
+ if [[ ! -e /var/lib/discourse-mail-receiver/api_key ]]; then
+ discourse-rake api_key:create_master[email-receiver] >/var/lib/discourse-mail-receiver/api_key
+ fi
+ '';
+ script =
+ let
+ apiKeyPath =
+ if cfg.mail.incoming.apiKeyFile == null then
+ "/var/lib/discourse-mail-receiver/api_key"
+ else
+ cfg.mail.incoming.apiKeyFile;
+ in ''
+ set -o errexit -o pipefail -o nounset -o errtrace
+ shopt -s inherit_errexit
+
+ export api_key=$(<'${apiKeyPath}')
+
+ jq <${mail-receiver-json} \
+ '.DISCOURSE_API_KEY = $ENV.api_key' \
+ >'/run/discourse-mail-receiver/mail-receiver-environment.json'
+ '';
+
+ serviceConfig = {
+ Type = "oneshot";
+ RemainAfterExit = true;
+ RuntimeDirectory = "discourse-mail-receiver";
+ RuntimeDirectoryMode = "0700";
+ StateDirectory = "discourse-mail-receiver";
+ User = "discourse";
+ Group = "discourse";
+ };
+ });
+
+ services.discourse.siteSettings = {
+ required = {
+ notification_email = cfg.mail.notificationEmailAddress;
+ contact_email = cfg.mail.contactEmailAddress;
+ };
+ email = {
+ manual_polling_enabled = cfg.mail.incoming.enable;
+ reply_by_email_enabled = cfg.mail.incoming.enable;
+ reply_by_email_address = cfg.mail.incoming.replyEmailAddress;
+ };
+ };
+
+ services.postfix = lib.mkIf cfg.mail.incoming.enable {
+ enable = true;
+ sslCert = if cfg.sslCertificate != null then cfg.sslCertificate else "";
+ sslKey = if cfg.sslCertificateKey != null then cfg.sslCertificateKey else "";
+
+ origin = cfg.hostname;
+ relayDomains = [ cfg.hostname ];
+ config = {
+ smtpd_recipient_restrictions = "check_policy_service unix:private/discourse-policy";
+ append_dot_mydomain = lib.mkDefault false;
+ compatibility_level = "2";
+ smtputf8_enable = false;
+ smtpd_banner = lib.mkDefault "ESMTP server";
+ myhostname = lib.mkDefault cfg.hostname;
+ mydestination = lib.mkDefault "localhost";
+ };
+ transport = ''
+ ${cfg.hostname} discourse-mail-receiver:
+ '';
+ masterConfig = {
+ "discourse-mail-receiver" = {
+ type = "unix";
+ privileged = true;
+ chroot = false;
+ command = "pipe";
+ args = [
+ "user=discourse"
+ "argv=${cfg.mail.incoming.mailReceiverPackage}/bin/receive-mail"
+ "\${recipient}"
+ ];
+ };
+ "discourse-policy" = {
+ type = "unix";
+ privileged = true;
+ chroot = false;
+ command = "spawn";
+ args = [
+ "user=discourse"
+ "argv=${cfg.mail.incoming.mailReceiverPackage}/bin/discourse-smtp-fast-rejection"
+ ];
+ };
+ };
+ };
+
+ users.users = {
+ discourse = {
+ group = "discourse";
+ isSystemUser = true;
+ };
+ } // (lib.optionalAttrs cfg.nginx.enable {
+ ${config.services.nginx.user}.extraGroups = [ "discourse" ];
+ });
+
+ users.groups = {
+ discourse = {};
+ };
+
+ environment.systemPackages = [
+ cfg.package.rake
+ ];
+ };
+}
From 3c7df2a3cb45ffa5a53e853df2d2b327c153cd81 Mon Sep 17 00:00:00 2001
From: talyz
Date: Sun, 14 Mar 2021 15:44:34 +0100
Subject: [PATCH 6/9] nixos/discourse: Add test
---
nixos/tests/all-tests.nix | 1 +
nixos/tests/discourse.nix | 197 ++++++++++++++++++++
pkgs/servers/web-apps/discourse/default.nix | 3 +-
3 files changed, 200 insertions(+), 1 deletion(-)
create mode 100644 nixos/tests/discourse.nix
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 65c7d84ee644..f37fdad5edf4 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -87,6 +87,7 @@ in
croc = handleTest ./croc.nix {};
deluge = handleTest ./deluge.nix {};
dhparams = handleTest ./dhparams.nix {};
+ discourse = handleTest ./discourse.nix {};
dnscrypt-proxy2 = handleTestOn ["x86_64-linux"] ./dnscrypt-proxy2.nix {};
dnscrypt-wrapper = handleTestOn ["x86_64-linux"] ./dnscrypt-wrapper {};
doas = handleTest ./doas.nix {};
diff --git a/nixos/tests/discourse.nix b/nixos/tests/discourse.nix
new file mode 100644
index 000000000000..3c965550fe0a
--- /dev/null
+++ b/nixos/tests/discourse.nix
@@ -0,0 +1,197 @@
+# This tests Discourse by:
+# 1. logging in as the admin user
+# 2. sending a private message to the admin user through the API
+# 3. replying to that message via email.
+
+import ./make-test-python.nix (
+ { pkgs, lib, ... }:
+ let
+ certs = import ./common/acme/server/snakeoil-certs.nix;
+ clientDomain = "client.fake.domain";
+ discourseDomain = certs.domain;
+ adminPassword = "eYAX85qmMJ5GZIHLaXGDAoszD7HSZp5d";
+ secretKeyBase = "381f4ac6d8f5e49d804dae72aa9c046431d2f34c656a705c41cd52fed9b4f6f76f51549f0b55db3b8b0dded7a00d6a381ebe9a4367d2d44f5e743af6628b4d42";
+ admin = {
+ email = "alice@${clientDomain}";
+ username = "alice";
+ fullName = "Alice Admin";
+ passwordFile = "${pkgs.writeText "admin-pass" adminPassword}";
+ };
+ in
+ {
+ name = "discourse";
+ meta = with pkgs.lib.maintainers; {
+ maintainers = [ talyz ];
+ };
+
+ nodes.discourse =
+ { nodes, ... }:
+ {
+ virtualisation.memorySize = 2048;
+
+ imports = [ common/user-account.nix ];
+
+ security.pki.certificateFiles = [
+ certs.ca.cert
+ ];
+
+ networking.extraHosts = ''
+ 127.0.0.1 ${discourseDomain}
+ ${nodes.client.config.networking.primaryIPAddress} ${clientDomain}
+ '';
+
+ services.postfix = {
+ enableSubmission = true;
+ enableSubmissions = true;
+ submissionsOptions = {
+ smtpd_sasl_auth_enable = "yes";
+ smtpd_client_restrictions = "permit";
+ };
+ };
+
+ environment.systemPackages = [ pkgs.jq ];
+
+ services.discourse = {
+ enable = true;
+ inherit admin;
+ hostname = discourseDomain;
+ sslCertificate = "${certs.${discourseDomain}.cert}";
+ sslCertificateKey = "${certs.${discourseDomain}.key}";
+ secretKeyBaseFile = "${pkgs.writeText "secret-key-base" secretKeyBase}";
+ enableACME = false;
+ mail.outgoing.serverAddress = clientDomain;
+ mail.incoming.enable = true;
+ siteSettings = {
+ posting = {
+ min_post_length = 5;
+ min_first_post_length = 5;
+ min_personal_message_post_length = 5;
+ };
+ };
+ unicornTimeout = 900;
+ };
+
+ networking.firewall.allowedTCPPorts = [ 25 465 ];
+ };
+
+ nodes.client =
+ { nodes, ... }:
+ {
+ imports = [ common/user-account.nix ];
+
+ security.pki.certificateFiles = [
+ certs.ca.cert
+ ];
+
+ networking.extraHosts = ''
+ 127.0.0.1 ${clientDomain}
+ ${nodes.discourse.config.networking.primaryIPAddress} ${discourseDomain}
+ '';
+
+ services.dovecot2 = {
+ enable = true;
+ protocols = [ "imap" ];
+ modules = [ pkgs.dovecot_pigeonhole ];
+ };
+
+ services.postfix = {
+ enable = true;
+ origin = clientDomain;
+ relayDomains = [ clientDomain ];
+ config = {
+ compatibility_level = "2";
+ smtpd_banner = "ESMTP server";
+ myhostname = clientDomain;
+ mydestination = clientDomain;
+ };
+ };
+
+ environment.systemPackages =
+ let
+ replyToEmail = pkgs.writeScriptBin "reply-to-email" ''
+ #!${pkgs.python3.interpreter}
+ import imaplib
+ import smtplib
+ import ssl
+ import email.header
+ from email import message_from_bytes
+ from email.message import EmailMessage
+
+ with imaplib.IMAP4('localhost') as imap:
+ imap.login('alice', 'foobar')
+ imap.select()
+ status, data = imap.search(None, 'ALL')
+ assert status == 'OK'
+
+ nums = data[0].split()
+ assert len(nums) == 1
+
+ status, msg_data = imap.fetch(nums[0], '(RFC822)')
+ assert status == 'OK'
+
+ msg = email.message_from_bytes(msg_data[0][1])
+ subject = str(email.header.make_header(email.header.decode_header(msg['Subject'])))
+ reply_to = email.header.decode_header(msg['Reply-To'])[0][0]
+ message_id = email.header.decode_header(msg['Message-ID'])[0][0]
+ date = email.header.decode_header(msg['Date'])[0][0]
+
+ ctx = ssl.create_default_context()
+ with smtplib.SMTP_SSL(host='${discourseDomain}', context=ctx) as smtp:
+ reply = EmailMessage()
+ reply['Subject'] = 'Re: ' + subject
+ reply['To'] = reply_to
+ reply['From'] = 'alice@${clientDomain}'
+ reply['In-Reply-To'] = message_id
+ reply['References'] = message_id
+ reply['Date'] = date
+ reply.set_content("Test reply.")
+
+ smtp.send_message(reply)
+ smtp.quit()
+ '';
+ in
+ [ replyToEmail ];
+
+ networking.firewall.allowedTCPPorts = [ 25 ];
+ };
+
+
+ testScript = { nodes }:
+ let
+ request = builtins.toJSON {
+ title = "Private message";
+ raw = "This is a test message.";
+ target_usernames = admin.username;
+ archetype = "private_message";
+ };
+ in ''
+ discourse.start()
+ client.start()
+
+ discourse.wait_for_unit("discourse.service")
+ discourse.wait_for_file("/run/discourse/sockets/unicorn.sock")
+ discourse.wait_until_succeeds("curl -sS -f https://${discourseDomain}")
+ discourse.succeed(
+ "curl -sS -f https://${discourseDomain}/session/csrf -c cookie -b cookie -H 'Accept: application/json' | jq -r '\"X-CSRF-Token: \" + .csrf' > csrf_token",
+ "curl -sS -f https://${discourseDomain}/session -c cookie -b cookie -H @csrf_token -H 'Accept: application/json' -d 'login=${nodes.discourse.config.services.discourse.admin.username}' -d \"password=${adminPassword}\" | jq -e '.user.username == \"${nodes.discourse.config.services.discourse.admin.username}\"'",
+ "curl -sS -f https://${discourseDomain}/login -v -H 'Accept: application/json' -c cookie -b cookie 2>&1 | grep ${nodes.discourse.config.services.discourse.admin.username}",
+ )
+
+ client.wait_for_unit("postfix.service")
+ client.wait_for_unit("dovecot2.service")
+
+ discourse.succeed(
+ "sudo -u discourse discourse-rake api_key:create_master[master] >api_key",
+ 'curl -sS -f https://${discourseDomain}/posts -X POST -H "Content-Type: application/json" -H "Api-Key: $(topic_id'
+ )
+ discourse.succeed(
+ 'curl -sS -f https://${discourseDomain}/t/$(Test reply.
" then true else null end\' '
+ )
+ '';
+ })
diff --git a/pkgs/servers/web-apps/discourse/default.nix b/pkgs/servers/web-apps/discourse/default.nix
index 8b1a2941eea6..900d69210923 100644
--- a/pkgs/servers/web-apps/discourse/default.nix
+++ b/pkgs/servers/web-apps/discourse/default.nix
@@ -1,4 +1,4 @@
-{ stdenv, makeWrapper, runCommandNoCC, lib
+{ stdenv, makeWrapper, runCommandNoCC, lib, nixosTests
, fetchFromGitHub, bundlerEnv, ruby, replace, gzip, gnutar, git
, util-linux, gawk, imagemagick, optipng, pngquant, libjpeg, jpegoptim
, gifsicle, libpsl, redis, postgresql, which, brotli, procps
@@ -228,6 +228,7 @@ let
passthru = {
inherit rubyEnv runtimeEnv runtimeDeps rake;
ruby = rubyEnv.wrappedRuby;
+ tests = nixosTests.discourse;
};
};
in discourse
From 5138941ad3ce5f0f364965206df1b2a0a43d0741 Mon Sep 17 00:00:00 2001
From: talyz
Date: Sun, 14 Mar 2021 18:01:17 +0100
Subject: [PATCH 7/9] discourse: Add updater script
---
pkgs/servers/web-apps/discourse/update.py | 164 ++++++++++++++++++++++
1 file changed, 164 insertions(+)
create mode 100755 pkgs/servers/web-apps/discourse/update.py
diff --git a/pkgs/servers/web-apps/discourse/update.py b/pkgs/servers/web-apps/discourse/update.py
new file mode 100755
index 000000000000..c401ab552bb7
--- /dev/null
+++ b/pkgs/servers/web-apps/discourse/update.py
@@ -0,0 +1,164 @@
+#!/usr/bin/env nix-shell
+#! nix-shell -i python3 -p bundix bundler nix-update python3 python3Packages.requests python3Packages.click python3Packages.click-log
+
+import click
+import click_log
+import shutil
+import tempfile
+import re
+import logging
+import subprocess
+import pathlib
+from distutils.version import LooseVersion
+from typing import Iterable
+
+import requests
+
+logger = logging.getLogger(__name__)
+
+
+class DiscourseRepo:
+ version_regex = re.compile(r'^v\d+\.\d+\.\d+$')
+ def __init__(self, owner: str = 'discourse', repo: str = 'discourse'):
+ self.owner = owner
+ self.repo = repo
+
+ @property
+ def tags(self) -> Iterable[str]:
+ r = requests.get(f'https://api.github.com/repos/{self.owner}/{self.repo}/git/refs/tags').json()
+ tags = [x['ref'].replace('refs/tags/', '') for x in r]
+
+ # filter out versions not matching version_regex
+ versions = list(filter(self.version_regex.match, tags))
+
+ # sort, but ignore v for sorting comparisons
+ versions.sort(key=lambda x: LooseVersion(x.replace('v', '')), reverse=True)
+ return versions
+
+ @staticmethod
+ def rev2version(tag: str) -> str:
+ """
+ normalize a tag to a version number.
+ This obviously isn't very smart if we don't pass something that looks like a tag
+ :param tag: the tag to normalize
+ :return: a normalized version number
+ """
+ # strip v prefix
+ return re.sub(r'^v', '', tag)
+
+ def get_file(self, filepath, rev):
+ """returns file contents at a given rev :param filepath: the path to
+ the file, relative to the repo root :param rev: the rev to
+ fetch at :return:
+
+ """
+ return requests.get(f'https://raw.githubusercontent.com/{self.owner}/{self.repo}/{rev}/{filepath}').text
+
+
+def _call_nix_update(pkg, version):
+ """calls nix-update from nixpkgs root dir"""
+ nixpkgs_path = pathlib.Path(__file__).parent / '../../../../'
+ return subprocess.check_output(['nix-update', pkg, '--version', version], cwd=nixpkgs_path)
+
+
+def _get_current_package_version(pkg: str):
+ nixpkgs_path = pathlib.Path(__file__).parent / '../../../../'
+ return subprocess.check_output(['nix', 'eval', '--raw', f'nixpkgs.{pkg}.version'], text=True)
+
+
+def _diff_file(filepath: str, old_version: str, new_version: str):
+ repo = DiscourseRepo()
+
+ current_dir = pathlib.Path(__file__).parent
+
+ old = repo.get_file(filepath, 'v' + old_version)
+ new = repo.get_file(filepath, 'v' + new_version)
+
+ if old == new:
+ click.secho(f'{filepath} is unchanged', fg='green')
+ return
+
+ with tempfile.NamedTemporaryFile(mode='w') as o, tempfile.NamedTemporaryFile(mode='w') as n:
+ o.write(old), n.write(new)
+ width = shutil.get_terminal_size((80, 20)).columns
+ diff_proc = subprocess.run(
+ ['diff', '--color=always', f'--width={width}', '-y', o.name, n.name],
+ stdout=subprocess.PIPE,
+ cwd=current_dir,
+ text=True
+ )
+
+ click.secho(f'Diff for {filepath} ({old_version} -> {new_version}):', fg='bright_blue', bold=True)
+ click.echo(diff_proc.stdout + '\n')
+ return
+
+
+@click_log.simple_verbosity_option(logger)
+
+
+@click.group()
+def cli():
+ pass
+
+
+@cli.command()
+@click.argument('rev', default='latest')
+@click.option('--reverse/--no-reverse', default=False, help='Print diffs from REV to current.')
+def print_diffs(rev, reverse):
+ """Print out diffs for files used as templates for the NixOS module.
+
+ The current package version found in the nixpkgs worktree the
+ script is run from will be used to download the "from" file and
+ REV used to download the "to" file for the diff, unless the
+ '--reverse' flag is specified.
+
+ REV should be the git rev to find changes in ('vX.Y.Z') or
+ 'latest'; defaults to 'latest'.
+
+ """
+ if rev == 'latest':
+ repo = DiscourseRepo()
+ rev = repo.tags[0]
+
+ old_version = _get_current_package_version('discourse')
+ new_version = DiscourseRepo.rev2version(rev)
+
+ if reverse:
+ old_version, new_version = new_version, old_version
+
+ for f in ['config/nginx.sample.conf', 'config/discourse_defaults.conf']:
+ _diff_file(f, old_version, new_version)
+
+
+@cli.command()
+@click.argument('rev', default='latest')
+def update(rev):
+ """Update gem files and version.
+
+ REV should be the git rev to update to ('vX.Y.Z') or 'latest';
+ defaults to 'latest'.
+
+ """
+ repo = DiscourseRepo()
+
+ if rev == 'latest':
+ rev = repo.tags[0]
+ logger.debug(f"Using rev {rev}")
+
+ version = repo.rev2version(rev)
+ logger.debug(f"Using version {version}")
+
+ rubyenv_dir = pathlib.Path(__file__).parent / "rubyEnv"
+
+ for fn in ['Gemfile.lock', 'Gemfile']:
+ with open(rubyenv_dir / fn, 'w') as f:
+ f.write(repo.get_file(fn, rev))
+
+ subprocess.check_output(['bundle', 'lock'], cwd=rubyenv_dir)
+ subprocess.check_output(['bundix'], cwd=rubyenv_dir)
+
+ _call_nix_update('discourse', repo.rev2version(rev))
+
+
+if __name__ == '__main__':
+ cli()
From 642854055c4315978dd470aa8eeb06b5333cdfa3 Mon Sep 17 00:00:00 2001
From: talyz
Date: Mon, 15 Mar 2021 16:55:05 +0100
Subject: [PATCH 8/9] nixos/discourse: Add NixOS manual entry
---
nixos/modules/services/web-apps/discourse.nix | 3 +
nixos/modules/services/web-apps/discourse.xml | 323 ++++++++++++++++++
2 files changed, 326 insertions(+)
create mode 100644 nixos/modules/services/web-apps/discourse.xml
diff --git a/nixos/modules/services/web-apps/discourse.nix b/nixos/modules/services/web-apps/discourse.nix
index 6f4c50006d3e..03ea002c9dee 100644
--- a/nixos/modules/services/web-apps/discourse.nix
+++ b/nixos/modules/services/web-apps/discourse.nix
@@ -1029,4 +1029,7 @@ in
cfg.package.rake
];
};
+
+ meta.doc = ./discourse.xml;
+ meta.maintainers = [ lib.maintainers.talyz ];
}
diff --git a/nixos/modules/services/web-apps/discourse.xml b/nixos/modules/services/web-apps/discourse.xml
new file mode 100644
index 000000000000..bae562423213
--- /dev/null
+++ b/nixos/modules/services/web-apps/discourse.xml
@@ -0,0 +1,323 @@
+
+ Discourse
+
+ Discourse is a
+ modern and open source discussion platform.
+
+
+
+ Basic usage
+
+ A minimal configuration using Let's Encrypt for TLS certificates looks like this:
+
+services.discourse = {
+ enable = true;
+ hostname = "discourse.example.com";
+ admin = {
+ email = "admin@example.com";
+ username = "admin";
+ fullName = "Administrator";
+ passwordFile = "/path/to/password_file";
+ };
+ secretKeyBaseFile = "/path/to/secret_key_base_file";
+};
+ security.acme.email = "me@example.com";
+ security.acme.acceptTerms = true;
+
+
+
+
+ Provided a proper DNS setup, you'll be able to connect to the
+ instance at discourse.example.com and log in
+ using the credentials provided in
+ services.discourse.admin .
+
+
+
+
+ Using a regular TLS certificate
+
+ To set up TLS using a regular certificate and key on file, use
+ the
+ and
+ options:
+
+
+services.discourse = {
+ enable = true;
+ hostname = "discourse.example.com";
+ sslCertificate = "/path/to/ssl_certificate";
+ sslCertificateKey = "/path/to/ssl_certificate_key";
+ admin = {
+ email = "admin@example.com";
+ username = "admin";
+ fullName = "Administrator";
+ passwordFile = "/path/to/password_file";
+ };
+ secretKeyBaseFile = "/path/to/secret_key_base_file";
+};
+
+
+
+
+
+
+ Database access
+
+ Discourse uses
+ PostgreSQL to store most of its
+ data. A database will automatically be enabled and a database
+ and role created unless is changed from
+ its default of null or is set
+ to false .
+
+
+
+ External database access can also be configured by setting
+ , and as
+ appropriate. Note that you need to manually create a database
+ called discourse (or the name you chose in
+ ) and
+ allow the configured database user full access to it.
+
+
+
+
+ Email
+
+ In addition to the basic setup, you'll want to configure an SMTP
+ server Discourse can use to send user
+ registration and password reset emails, among others. You can
+ also optionally let Discourse receive
+ email, which enables people to reply to threads and conversations
+ via email.
+
+
+
+ A basic setup which assumes you want to use your configured hostname as
+ email domain can be done like this:
+
+
+services.discourse = {
+ enable = true;
+ hostname = "discourse.example.com";
+ sslCertificate = "/path/to/ssl_certificate";
+ sslCertificateKey = "/path/to/ssl_certificate_key";
+ admin = {
+ email = "admin@example.com";
+ username = "admin";
+ fullName = "Administrator";
+ passwordFile = "/path/to/password_file";
+ };
+ mail.outgoing = {
+ serverAddress = "smtp.emailprovider.com";
+ port = 587;
+ username = "user@emailprovider.com";
+ passwordFile = "/path/to/smtp_password_file";
+ };
+ mail.incoming.enable = true;
+ secretKeyBaseFile = "/path/to/secret_key_base_file";
+};
+
+
+ This assumes you have set up an MX record for the address you've
+ set in hostname and
+ requires proper SPF, DKIM and DMARC configuration to be done for
+ the domain you're sending from, in order for email to be reliably delivered.
+
+
+
+ If you want to use a different domain for your outgoing email
+ (for example example.com instead of
+ discourse.example.com ) you should set
+ and
+ manually.
+
+
+
+
+ Setup of TLS for incoming email is currently only configured
+ automatically when a regular TLS certificate is used, i.e. when
+ and
+ are
+ set.
+
+
+
+
+
+
+ Additional settings
+
+ Additional site settings and backend settings, for which no
+ explicit NixOS options are provided,
+ can be set in and
+ respectively.
+
+
+
+ Site settings
+
+ Site settings
are the settings that can be
+ changed through the Discourse
+ UI. Their default values can be set using
+ .
+
+
+
+ Settings are expressed as a Nix attribute set which matches the
+ structure of the configuration in
+ config/site_settings.yml.
+ To find a setting's path, you only need to care about the first
+ two levels; i.e. its category (e.g. login )
+ and name (e.g. invite_only ).
+
+
+
+ Settings containing secret data should be set to an attribute
+ set containing the attribute _secret - a
+ string pointing to a file containing the value the option
+ should be set to. See the example.
+
+
+
+
+ Backend settings
+
+ Settings are expressed as a Nix attribute set which matches the
+ structure of the configuration in
+ config/discourse.conf.
+ Empty parameters can be defined by setting them to
+ null .
+
+
+
+
+ Example
+
+ The following example sets the title and description of the
+ Discourse instance and enables
+ GitHub login in the site settings,
+ and changes a few request limits in the backend settings:
+
+services.discourse = {
+ enable = true;
+ hostname = "discourse.example.com";
+ sslCertificate = "/path/to/ssl_certificate";
+ sslCertificateKey = "/path/to/ssl_certificate_key";
+ admin = {
+ email = "admin@example.com";
+ username = "admin";
+ fullName = "Administrator";
+ passwordFile = "/path/to/password_file";
+ };
+ mail.outgoing = {
+ serverAddress = "smtp.emailprovider.com";
+ port = 587;
+ username = "user@emailprovider.com";
+ passwordFile = "/path/to/smtp_password_file";
+ };
+ mail.incoming.enable = true;
+ siteSettings = {
+ required = {
+ title = "My Cats";
+ site_description = "Discuss My Cats (and be nice plz)";
+ };
+ login = {
+ enable_github_logins = true;
+ github_client_id = "a2f6dfe838cb3206ce20";
+ github_client_secret._secret = /run/keys/discourse_github_client_secret;
+ };
+ };
+ backendSettings = {
+ max_reqs_per_ip_per_minute = 300;
+ max_reqs_per_ip_per_10_seconds = 60;
+ max_asset_reqs_per_ip_per_10_seconds = 250;
+ max_reqs_per_ip_mode = "warn+block";
+ };
+ secretKeyBaseFile = "/path/to/secret_key_base_file";
+};
+
+
+
+ In the resulting site settings file, the
+ login.github_client_secret key will be set
+ to the contents of the
+ /run/keys/discourse_github_client_secret
+ file.
+
+
+
+
+ Plugins
+
+ You can install Discourse plugins
+ using the
+ option. As long as a plugin supports the standard install
+ method, packaging it should only require fetching its source
+ with an appropriate fetcher.
+
+
+
+ Some plugins provide site
+ settings. Their defaults can be configured using , just like
+ regular site settings. To find the names of these settings, look
+ in the config/settings.yml file of the plugin
+ repo.
+
+
+
+ For example, to add the discourse-spoiler-alert
+ plugin and disable it by default:
+
+
+services.discourse = {
+ enable = true;
+ hostname = "discourse.example.com";
+ sslCertificate = "/path/to/ssl_certificate";
+ sslCertificateKey = "/path/to/ssl_certificate_key";
+ admin = {
+ email = "admin@example.com";
+ username = "admin";
+ fullName = "Administrator";
+ passwordFile = "/path/to/password_file";
+ };
+ mail.outgoing = {
+ serverAddress = "smtp.emailprovider.com";
+ port = 587;
+ username = "user@emailprovider.com";
+ passwordFile = "/path/to/smtp_password_file";
+ };
+ mail.incoming.enable = true;
+ plugins = [
+ (pkgs.fetchFromGitHub {
+ owner = "discourse";
+ repo = "discourse-spoiler-alert";
+ rev = "e200cfa571d252cab63f3d30d619b370986e4cee";
+ sha256 = "0ya69ix5g77wz4c9x9gmng6l25ghb5xxlx3icr6jam16q14dzc33";
+ })
+ ];
+ siteSettings = {
+ plugins = {
+ spoiler_enabled = false;
+ };
+ };
+ secretKeyBaseFile = "/path/to/secret_key_base_file";
+};
+
+
+
+
+
From bc26a46943fc967b5a8083311c85371094db8f91 Mon Sep 17 00:00:00 2001
From: talyz
Date: Mon, 29 Mar 2021 13:45:38 +0200
Subject: [PATCH 9/9] rl-2105: Add Discourse module note
---
nixos/doc/manual/release-notes/rl-2105.xml | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/nixos/doc/manual/release-notes/rl-2105.xml b/nixos/doc/manual/release-notes/rl-2105.xml
index b7947293c012..e2c39a71064d 100644
--- a/nixos/doc/manual/release-notes/rl-2105.xml
+++ b/nixos/doc/manual/release-notes/rl-2105.xml
@@ -109,6 +109,16 @@
Web Services Dynamic Discovery host daemon
+
+
+ Discourse, a
+ modern and open source discussion platform.
+
+
+ See the Discourse
+ section of the NixOS manual for more information.
+
+