nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at python-updates 543 lines 15 kB view raw
1{ 2 stdenv, 3 pkgs, 4 makeWrapper, 5 runCommand, 6 lib, 7 writeShellScript, 8 fetchFromGitHub, 9 bundlerEnv, 10 callPackage, 11 nixosTests, 12 13 defaultGemConfig, 14 ruby_3_3, 15 gzip, 16 gnutar, 17 git, 18 cacert, 19 util-linux, 20 gawk, 21 net-tools, 22 imagemagick, 23 optipng, 24 pngquant, 25 libjpeg, 26 jpegoptim, 27 gifsicle, 28 jhead, 29 oxipng, 30 libpsl, 31 redis, 32 postgresql, 33 which, 34 brotli, 35 procps, 36 rsync, 37 icu, 38 rustPlatform, 39 buildRubyGem, 40 rustc, 41 cargo, 42 pnpm_9, 43 fetchPnpmDeps, 44 pnpmConfigHook, 45 svgo, 46 nodejs_22, 47 jq, 48 moreutils, 49 terser, 50 uglify-js, 51 52 plugins ? [ ], 53}: 54 55let 56 version = "2025.12.0"; 57 58 src = fetchFromGitHub { 59 owner = "discourse"; 60 repo = "discourse"; 61 rev = "v${version}"; 62 sha256 = "sha256-zURRueaYWl1FqOI12H+S/ssMhN38nDuWRBqb579w6HQ="; 63 }; 64 65 ruby = ruby_3_3; 66 67 runtimeDeps = [ 68 # For backups, themes and assets 69 rubyEnv.wrappedRuby 70 rsync 71 gzip 72 gnutar 73 git 74 brotli 75 nodejs_22 76 77 # Misc required system utils 78 which 79 procps # For ps and kill 80 util-linux # For renice 81 gawk 82 net-tools # For hostname 83 84 # Image optimization 85 imagemagick 86 optipng 87 oxipng 88 pngquant 89 libjpeg 90 jpegoptim 91 gifsicle 92 svgo 93 jhead 94 ]; 95 96 runtimeEnv = { 97 HOME = "/run/discourse/home"; 98 RAILS_ENV = "production"; 99 UNICORN_LISTENER = "/run/discourse/sockets/unicorn.sock"; 100 }; 101 102 mkDiscoursePlugin = 103 { 104 name ? null, 105 pname ? null, 106 version ? null, 107 meta ? null, 108 bundlerEnvArgs ? { }, 109 preserveGemsDir ? false, 110 src, 111 ... 112 }@args: 113 let 114 rubyEnv = bundlerEnv ( 115 bundlerEnvArgs 116 // { 117 inherit 118 name 119 pname 120 version 121 ruby 122 ; 123 } 124 ); 125 in 126 stdenv.mkDerivation ( 127 # Allow overriding the plugin name 128 { 129 pluginName = if name != null then name else "${pname}-${version}"; 130 } 131 // removeAttrs args [ "bundlerEnvArgs" ] 132 // { 133 dontConfigure = true; 134 dontBuild = true; 135 installPhase = '' 136 runHook preInstall 137 mkdir -p $out 138 cp -r * $out/ 139 '' 140 + lib.optionalString (bundlerEnvArgs != { }) ( 141 if preserveGemsDir then 142 '' 143 cp -r ${rubyEnv}/lib/ruby/gems/* $out/gems/ 144 '' 145 else 146 '' 147 if [[ -e $out/gems ]]; then 148 echo "Warning: The repo contains a 'gems' directory which will be removed!" 149 echo " If you need to preserve it, set 'preserveGemsDir = true'." 150 rm -r $out/gems 151 fi 152 ln -sf ${rubyEnv}/lib/ruby/gems $out/gems 153 '' 154 + '' 155 runHook postInstall 156 '' 157 ); 158 } 159 ); 160 161 rake = 162 runCommand "discourse-rake" 163 { 164 nativeBuildInputs = [ makeWrapper ]; 165 } 166 '' 167 mkdir -p $out/bin 168 makeWrapper ${rubyEnv}/bin/rake $out/bin/discourse-rake \ 169 ${ 170 lib.concatStrings (lib.mapAttrsToList (name: value: "--set ${name} '${value}' ") runtimeEnv) 171 } \ 172 --prefix PATH : ${lib.makeBinPath runtimeDeps} \ 173 --set RAKEOPT '-f ${discourse}/share/discourse/Rakefile' \ 174 --chdir '${discourse}/share/discourse' 175 ''; 176 177 rubyEnv = bundlerEnv { 178 name = "discourse-ruby-env-${version}"; 179 inherit version ruby; 180 gemdir = ./rubyEnv; 181 gemset = import ./rubyEnv/gemset.nix; 182 gemConfig = defaultGemConfig // { 183 mini_racer = attrs: { 184 buildInputs = [ icu ]; 185 dontBuild = false; 186 NIX_LDFLAGS = "-licui18n"; 187 }; 188 libv8-node = 189 attrs: 190 let 191 noopScript = writeShellScript "noop" "exit 0"; 192 linkFiles = writeShellScript "link-files" '' 193 cd ../.. 194 195 mkdir -p vendor/v8/${stdenv.hostPlatform.system}/libv8/obj/ 196 ln -s "${nodejs_22.libv8}/lib/libv8.a" vendor/v8/${stdenv.hostPlatform.system}/libv8/obj/libv8_monolith.a 197 198 ln -s ${nodejs_22.libv8}/include vendor/v8/include 199 200 mkdir -p ext/libv8-node 201 echo '--- !ruby/object:Libv8::Node::Location::Vendor {}' >ext/libv8-node/.location.yml 202 ''; 203 in 204 { 205 dontBuild = false; 206 postPatch = '' 207 cp ${noopScript} libexec/build-libv8 208 cp ${noopScript} libexec/build-monolith 209 cp ${noopScript} libexec/download-node 210 cp ${noopScript} libexec/extract-node 211 cp ${linkFiles} libexec/inject-libv8 212 ''; 213 }; 214 mini_suffix = attrs: { 215 propagatedBuildInputs = [ libpsl ]; 216 dontBuild = false; 217 # Use our libpsl instead of the vendored one, which isn't 218 # available for aarch64. It has to be called 219 # libpsl.x86_64.so or it isn't found. 220 postPatch = '' 221 cp $(readlink -f ${lib.getLib libpsl}/lib/libpsl.so) vendor/libpsl.x86_64.so 222 ''; 223 }; 224 tokenizers = attrs: { 225 cargoDeps = rustPlatform.fetchCargoVendor { 226 inherit (buildRubyGem { inherit (attrs) gemName version source; }) 227 name 228 src 229 unpackPhase 230 nativeBuildInputs 231 ; 232 hash = "sha256-Yxcerq4Wil1nrEzHoEmsTAj4VnUmrwRlA3WO2b72yOc="; 233 }; 234 235 dontBuild = false; 236 237 nativeBuildInputs = [ 238 cargo 239 rustc 240 rustPlatform.cargoSetupHook 241 rustPlatform.bindgenHook 242 ]; 243 244 disallowedReferences = [ 245 rustc.unwrapped 246 ]; 247 248 preInstall = '' 249 export CARGO_HOME="$PWD/../.cargo/" 250 ''; 251 252 postInstall = '' 253 find $out -type f -name .rustc_info.json -delete 254 ''; 255 }; 256 tiktoken_ruby = attrs: { 257 cargoDeps = rustPlatform.fetchCargoVendor { 258 inherit (buildRubyGem { inherit (attrs) gemName version source; }) 259 name 260 src 261 unpackPhase 262 nativeBuildInputs 263 ; 264 hash = "sha256-zyGK+XJpMls6w0Uydegqsj4TH4IrVxANm0qmpR7+95I="; 265 }; 266 267 dontBuild = false; 268 269 nativeBuildInputs = [ 270 cargo 271 rustc 272 rustPlatform.cargoSetupHook 273 rustPlatform.bindgenHook 274 ]; 275 276 disallowedReferences = [ 277 rustc.unwrapped 278 ]; 279 280 preInstall = '' 281 export CARGO_HOME="$PWD/../.cargo/" 282 ''; 283 284 postInstall = '' 285 #ls $GEM_HOME/gems/${attrs.gemName}-${attrs.version}/lib 286 #mv -v $GEM_HOME/gems/${attrs.gemName}-${attrs.version}/lib/{glfm_markdown/glfm_markdown.so,} 287 find $out -type f -name .rustc_info.json -delete 288 ''; 289 }; 290 }; 291 292 groups = [ 293 "default" 294 "assets" 295 "development" 296 "test" 297 ]; 298 }; 299 300 assets = stdenv.mkDerivation { 301 pname = "discourse-assets"; 302 inherit version src; 303 304 pnpmDeps = fetchPnpmDeps { 305 pname = "discourse-assets"; 306 inherit version src; 307 pnpm = pnpm_9; 308 fetcherVersion = 1; 309 hash = "sha256-/GJQqbmBXn5SSdxQ3TBQEUGe6Qm7aJ1ogoYqOFD5Pm0="; 310 }; 311 312 nativeBuildInputs = runtimeDeps ++ [ 313 (postgresql.withPackages (ps: [ 314 ps.pgvector 315 ])) 316 redis 317 uglify-js 318 terser 319 jq 320 moreutils 321 nodejs_22 322 pnpmConfigHook 323 pnpm_9 324 ]; 325 326 outputs = [ 327 "out" 328 "node_modules" 329 "generated" 330 "frontend" 331 ]; 332 333 patches = [ 334 # Use the Ruby API version in the plugin gem path, to match the 335 # one constructed by bundlerEnv 336 ./plugin_gem_api_version.patch 337 338 # Change the path to the auto generated plugin assets, which 339 # defaults to the plugin's directory and isn't writable at the 340 # time of asset generation 341 ./auto_generated_path.patch 342 343 # Fix the rake command used to recursively execute itself in the 344 # assets precompilation task. 345 ./assets_rake_command.patch 346 347 # Little does he know, so he decided there is no need to generate the 348 # theme-transpiler over and over again. Which at the same time allows the removal 349 # of javascript devDependencies from the runtime environment. 350 ./prebuild-asset-processor.patch 351 ]; 352 353 env.RAILS_ENV = "production"; 354 env.DISCOURSE_DOWNLOAD_PRE_BUILT_ASSETS = "0"; 355 # Allow to use different bundler version than the lockfile has 356 env.BUNDLER_VERSION = pkgs.bundler.version; 357 358 # requires full git and repository, even a src `leaveDotGit` is not enough. So patch this function to return the version 359 postPatch = '' 360 substituteInPlace script/assemble_ember_build.rb --replace-fail "def core_tree_hash" "def core_tree_hash; return \"v${version}\"" 361 ''; 362 363 # We have to set up an environment that is close enough to 364 # production ready or the assets:precompile task refuses to 365 # run. This means that Redis and PostgreSQL has to be running and 366 # database migrations performed. 367 preBuild = '' 368 # Patch before running postinstall hook script 369 patchShebangs node_modules/ 370 patchShebangs --build frontend/ 371 export SSL_CERT_FILE=${cacert}/etc/ssl/certs/ca-bundle.crt 372 373 redis-server >/dev/null & 374 375 initdb -A trust $NIX_BUILD_TOP/postgres >/dev/null 376 postgres -D $NIX_BUILD_TOP/postgres -k $NIX_BUILD_TOP >/dev/null & 377 export PGHOST=$NIX_BUILD_TOP 378 379 echo "Waiting for Redis and PostgreSQL to be ready.." 380 while ! redis-cli --scan >/dev/null || ! psql -l >/dev/null; do 381 sleep 0.1 382 done 383 384 psql -d postgres -tAc 'CREATE USER "discourse"' 385 psql -d postgres -tAc 'CREATE DATABASE "discourse" OWNER "discourse"' 386 psql 'discourse' -tAc "CREATE EXTENSION IF NOT EXISTS pg_trgm" 387 psql 'discourse' -tAc "CREATE EXTENSION IF NOT EXISTS hstore" 388 psql 'discourse' -tAc "CREATE EXTENSION IF NOT EXISTS vector" 389 390 ${lib.concatMapStringsSep "\n" (p: "ln -sf ${p} plugins/${p.pluginName or ""}") plugins} 391 392 bundle exec rake db:migrate >/dev/null 393 chmod -R +w tmp 394 ''; 395 396 buildPhase = '' 397 runHook preBuild 398 399 patchShebangs script/ 400 bundle exec rake assets:precompile 401 402 runHook postBuild 403 ''; 404 405 installPhase = '' 406 runHook preInstall 407 408 mv public/assets $out 409 410 mv node_modules $node_modules 411 412 mv app/assets/generated $generated 413 mv frontend $frontend 414 415 runHook postInstall 416 ''; 417 418 # The node_modules output by design has broken symlinks, as it refers to the source code. 419 # They are resolved in the primary discourse derivation. 420 dontCheckForBrokenSymlinks = true; 421 }; 422 423 discourse = stdenv.mkDerivation { 424 pname = "discourse"; 425 inherit version src; 426 427 buildInputs = [ 428 rubyEnv 429 rubyEnv.wrappedRuby 430 rubyEnv.bundler 431 ]; 432 433 patches = [ 434 # Load a separate NixOS site settings file 435 ./nixos_defaults.patch 436 437 # Add a noninteractive admin creation task 438 ./admin_create.patch 439 440 # Add the path to the CA cert bundle to make TLS work 441 ./action_mailer_ca_cert.patch 442 443 # Log Unicorn messages to the journal and make request timeout 444 # configurable 445 ./unicorn_logging_and_timeout.patch 446 447 # Use the Ruby API version in the plugin gem path, to match the 448 # one constructed by bundlerEnv 449 ./plugin_gem_api_version.patch 450 451 # Change the path to the auto generated plugin assets, which 452 # defaults to the plugin's directory and isn't writable at the 453 # time of asset generation 454 ./auto_generated_path.patch 455 456 # Make sure the notification email setting applies 457 ./notification_email.patch 458 459 # Little does he know, so he decided there is no need to generate the 460 # theme-transpiler over and over again. Which at the same time allows the removal 461 # of javascript devDependencies from the runtime environment. 462 ./prebuild-asset-processor.patch 463 464 # Our app/assets/generated folder is a symlink, but the ruby File.mkdir_p doesn't allow 465 # a symlink in the way to the last directory. This patch explicitly resolves the symlink. 466 ./resolve_generated_assets_symlink.patch 467 ]; 468 469 postPatch = '' 470 # Always require lib-files and application.rb through their store 471 # path, not their relative state directory path. This gets rid of 472 # warnings and means we don't have to link back to lib from the 473 # state directory. 474 find config -type f -name "*.rb" -execdir \ 475 sed -Ei "s,(\.\./)+(lib|app)/,$out/share/discourse/\2/," {} \; 476 find config -maxdepth 1 -type f -name "*.rb" -execdir \ 477 sed -Ei "s,require_relative (\"|')([[:alnum:]].*)(\"|'),require_relative '$out/share/discourse/config/\2'," {} \; 478 ''; 479 480 buildPhase = '' 481 runHook preBuild 482 483 mv config config.dist 484 mv public public.dist 485 486 runHook postBuild 487 ''; 488 489 installPhase = '' 490 runHook preInstall 491 492 mkdir -p $out/share 493 cp -r . $out/share/discourse 494 rm -r $out/share/discourse/log 495 ln -sf /var/log/discourse $out/share/discourse/log 496 ln -sf /var/lib/discourse/tmp $out/share/discourse/tmp 497 ln -sf /run/discourse/config $out/share/discourse/config 498 ln -sf /run/discourse/public $out/share/discourse/public 499 ln -sf /run/discourse/assets-generated $out/share/discourse/app/assets/generated 500 ln -sf ${assets.node_modules} $out/share/discourse/node_modules 501 ln -sf ${assets} $out/share/discourse/public.dist/assets 502 # This needs to be copied because it contains symlinks to node_modules 503 rm -r $out/share/discourse/frontend 504 cp -r ${assets.frontend} $out/share/discourse/frontend 505 ${lib.concatMapStringsSep "\n" ( 506 p: "ln -sf ${p} $out/share/discourse/plugins/${p.pluginName or ""}" 507 ) plugins} 508 509 runHook postInstall 510 ''; 511 512 passthru = { 513 inherit 514 rubyEnv 515 runtimeEnv 516 runtimeDeps 517 rake 518 mkDiscoursePlugin 519 assets 520 ; 521 inherit (pkgs) 522 discourseAllPlugins 523 ; 524 enabledPlugins = plugins; 525 plugins = callPackage ./plugins/all-plugins.nix { inherit mkDiscoursePlugin; }; 526 ruby = rubyEnv.wrappedRuby; 527 tests = { 528 inherit (nixosTests) 529 discourse 530 discourseAllPlugins 531 ; 532 }; 533 }; 534 meta = { 535 homepage = "https://www.discourse.org/"; 536 platforms = lib.platforms.linux; 537 maintainers = with lib.maintainers; [ talyz ]; 538 license = lib.licenses.gpl2Plus; 539 description = "Open source discussion platform"; 540 }; 541 }; 542in 543discourse