{ bundlerEnv, cacert, defaultGemConfig, fetchFromGitLab, fetchYarnDeps, yarnConfigHook, git, gitlabEnterprise ? false, lib, makeWrapper, net-tools, nixosTests, nodejs_22, ruby_3_3, stdenv, tzdata, # gem dependencies: # gitlab-glfm-markdown buildRubyGem, cargo, rustc, rustPlatform, # gpgme pkg-config, # openssl openssl, # ruby-magic file, # static-holmes icu, which, zlib, }: let data = lib.importJSON ./data.json; version = data.version; src = fetchFromGitLab { owner = data.owner; repo = data.repo; rev = data.rev; hash = data.repo_hash; }; rubyEnv = bundlerEnv rec { name = "gitlab-env-${version}"; ruby = ruby_3_3; gemdir = ./rubyEnv; gemset = import (gemdir + "/gemset.nix") src; gemConfig = defaultGemConfig // { gpgme = attrs: { nativeBuildInputs = [ pkg-config ]; }; # the openssl needs the openssl include files openssl = attrs: { buildInputs = [ openssl ]; }; ruby-magic = attrs: { buildInputs = [ file ]; buildFlags = [ "--enable-system-libraries" ]; }; gitlab-glaz = attrs: { cargoDeps = rustPlatform.fetchCargoVendor { src = stdenv.mkDerivation { inherit (buildRubyGem { inherit (attrs) gemName version source; }) name src unpackPhase nativeBuildInputs ; dontBuilt = true; installPhase = '' cp -R ext/glaz $out cp Cargo.lock $out ''; }; hash = "sha256-5fGoW6TpkIQ8OIXjt2fLGzG9xhZ2TT+v2zLH1ecItII="; }; dontBuild = false; nativeBuildInputs = [ cargo rustc rustPlatform.cargoSetupHook rustPlatform.bindgenHook ]; disallowedReferences = [ rustc.unwrapped ]; preInstall = '' export CARGO_HOME="$PWD/../.cargo/" ''; postInstall = '' find $out -type f -name .rustc_info.json -delete ''; }; gitlab-glfm-markdown = attrs: { cargoDeps = rustPlatform.fetchCargoVendor { src = stdenv.mkDerivation { inherit (buildRubyGem { inherit (attrs) gemName version source; }) name src unpackPhase nativeBuildInputs ; dontBuilt = true; installPhase = '' cp -R ext/gitlab_glfm_markdown $out rm $out/Cargo.lock cp Cargo.lock $out ''; }; hash = "sha256-ikizLu1B+stdk+HDGjrACOpgptg0jfbHcoqfrJtUpEY="; }; dontBuild = false; nativeBuildInputs = [ cargo rustc rustPlatform.cargoSetupHook rustPlatform.bindgenHook ]; disallowedReferences = [ rustc.unwrapped ]; preInstall = '' export CARGO_HOME="$PWD/../.cargo/" ''; postInstall = '' find $out -type f -name .rustc_info.json -delete ''; }; gitlab_query_language = attrs: rec { cargoDeps = rustPlatform.fetchCargoVendor { src = stdenv.mkDerivation { inherit (buildRubyGem { inherit (attrs) gemName version source; }) name src unpackPhase nativeBuildInputs ; installPhase = '' mkdir -p $out cp Cargo.lock $out cp -R ext/gitlab_query_language/* $out ''; }; # GitLab publishes a Cargo.lock for gitlab_query_language that does not contain the `source` attribute # for the `glql` dependency. This is an intentional choice by them that is documented in the README. # This code refetches this hash and exposes the lockfile, so that it can be used in later stages. nativeBuildInputs = [ cargo ]; postPatch = '' export CARGO_HOME="$PWD/../.cargo/" cargo fetch ''; postBuild = '' cp Cargo.lock $out ''; hash = "sha256-v0XIAyBqwFpO4n6EUgXaE7/yuyYAva8arv4unVPqqN4="; }; postPatch = '' cp ${cargoDeps}/Cargo.lock . ''; dontBuild = false; nativeBuildInputs = [ cargo rustc rustPlatform.cargoSetupHook rustPlatform.bindgenHook ]; # The gem builder wrongly copies Cargo.lock within the gem, while it's missing in the outer rust project # before rustc ran. # This fixes this and makes the rustc invocation work. preInstall = '' export CARGO_HOME="$PWD/../.cargo/" # Unpack tar xvf $gempkg # Modify gzip -d data.tar.gz chmod +w data.tar mv Cargo.lock ./ext/gitlab_query_language tar rf data.tar ./ext/gitlab_query_language/Cargo.lock # Repack gzip data.tar tar -cf $gempkg data.tar.gz metadata.gz ''; postInstall = '' find $out -type f -name .rustc_info.json -delete ''; }; prometheus-client-mmap = attrs: { dontBuild = false; postPatch = let getconf = if stdenv.hostPlatform.isGnu then stdenv.cc.libc else getconf; in '' substituteInPlace lib/prometheus/client/page_size.rb --replace "getconf" "${lib.getBin getconf}/bin/getconf" ''; cargoDeps = rustPlatform.fetchCargoVendor { src = stdenv.mkDerivation { inherit (buildRubyGem { inherit (attrs) gemName version source; }) name src unpackPhase nativeBuildInputs ; dontBuilt = true; installPhase = '' cp -R ext/fast_mmaped_file_rs $out rm $out/Cargo.lock cp Cargo.lock $out ''; }; hash = "sha256-lhD8vlqK9a38ZLBD6YagWnJ/DQ8YqbC1NxEyzYnoLh8="; }; nativeBuildInputs = [ cargo rustc rustPlatform.cargoSetupHook rustPlatform.bindgenHook ]; disallowedReferences = [ rustc.unwrapped ]; preInstall = '' export CARGO_HOME="$PWD/../.cargo/" ''; postInstall = '' find $out -type f -name .rustc_info.json -delete ''; }; static_holmes = attrs: { nativeBuildInputs = [ icu which zlib.dev ]; }; }; groups = [ "default" "unicorn" "ed25519" "metrics" "development" "puma" "test" "kerberos" "opentelemetry" ]; # N.B. omniauth_oauth2_generic and apollo_upload_server both provide a # `console` executable. ignoreCollisions = true; extraConfigPaths = [ "${src}/vendor" "${src}/gems" ]; }; assets = stdenv.mkDerivation { pname = "gitlab-assets"; inherit version src; yarnOfflineCache = fetchYarnDeps { yarnLock = src + "/yarn.lock"; hash = data.yarn_hash; }; frontendIslandsYarnOfflineCache = fetchYarnDeps { # Revert to this when GitLab fixes their frontend_islands yarn.lock yarnLock = src + "/ee/frontend_islands/yarn.lock"; hash = data.frontend_islands_yarn_hash; }; nativeBuildInputs = [ rubyEnv.wrappedRuby rubyEnv.bundler nodejs_22 git cacert yarnConfigHook ]; patches = [ # Since version 12.6.0, the rake tasks need the location of git, # so we have to apply the location patches here too. ./remove-hardcoded-locations.patch # Gitlab edited the default database config since [1] and the # installer now complains about valid keywords only being "main", "ci" and "embedded". # # [1]: https://gitlab.com/gitlab-org/gitlab/-/commit/99c0fac52b10cd9df62bbe785db799352a2d9028 ./Remove-unsupported-database-names.patch ]; env = { # One of the patches uses this variable - if it's unset, execution # of rake tasks fails. GITLAB_LOG_PATH = "log"; FOSS_ONLY = !gitlabEnterprise; SKIP_FRONTEND_ISLANDS_BUILD = lib.optionalString (!gitlabEnterprise) "true"; SKIP_YARN_INSTALL = 1; NODE_OPTIONS = "--max-old-space-size=8192"; }; postConfigure = '' # Some rake tasks try to run yarn automatically, which won't work rm lib/tasks/yarn.rake # The rake tasks won't run without a basic configuration in place mv config/database.yml.postgresql config/database.yml mv config/gitlab.yml.example config/gitlab.yml patchShebangs scripts/frontend/ patchShebangs scripts/ '' + lib.optionalString gitlabEnterprise '' # Get node modules for frontend islands export HOME=$(mktemp -d) pushd ee/frontend_islands yarn config --offline set yarn-offline-mirror "$frontendIslandsYarnOfflineCache" fixup-yarn-lock yarn.lock yarn install \ --frozen-lockfile \ --force \ --production=false \ --ignore-engines \ --ignore-platform \ --ignore-scripts \ --no-progress \ --non-interactive \ --offline patchShebangs node_modules popd ''; buildPhase = '' runHook preBuild # TODO: Try to yarn install without --ignore-scripts # Needed for the js dependency pinia to work pushd node_modules/vue-demi yarn run postinstall popd # Apply node_modules patches node scripts/frontend/postinstall.js # Creates a `infection_scanner.json` needed for the assets compiler to succeed. node scripts/frontend/infection_scanner/infection_scanner.mjs bundle exec rake gitlab:assets:compile RAILS_ENV=production NODE_ENV=production SKIP_YARN_INSTALL=true runHook postBuild ''; installPhase = '' runHook preInstall mv public/assets $out runHook postInstall ''; }; in stdenv.mkDerivation { pname = "gitlab${lib.optionalString gitlabEnterprise "-ee"}"; inherit src version; nativeBuildInputs = [ makeWrapper ]; buildInputs = [ rubyEnv rubyEnv.wrappedRuby rubyEnv.bundler tzdata git net-tools ]; patches = [ # Change hardcoded paths to the NixOS equivalent ./remove-hardcoded-locations.patch ]; postPatch = '' ${lib.optionalString (!gitlabEnterprise) '' # Remove all proprietary components rm -rf ee sed -i 's/-ee//' ./VERSION ''} # For reasons I don't understand "bundle exec" ignores the # RAILS_ENV causing tests to be executed that fail because we're # not installing development and test gems above. Deleting the # tests works though. rm lib/tasks/test.rake rm config/initializers/gitlab_shell_secret_token.rb sed -i '/ask_to_continue/d' lib/tasks/gitlab/two_factor.rake sed -ri -e '/log_level/a config.logger = Logger.new(STDERR)' config/environments/production.rb mv config/puma.rb.example config/puma.rb # 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 -exec sed -i -e "s|\.\./\.\./lib|$out/share/gitlab/lib|" {} + find config -type f -exec sed -i -e "s|\.\./lib|$out/share/gitlab/lib|" {} + find config -type f -exec sed -i -e "s|require_relative 'application'|require_relative '$out/share/gitlab/config/application'|" {} + find config -type f -exec sed -i -e "s|require_relative \"/home/git/gitlab/lib/gitlab/puma/error_handler\"|require_relative \"$out/share/gitlab/lib/gitlab/puma/error_handler\"|" {} + ''; buildPhase = '' rm -f config/secrets.yml mv config config.dist rm -r tmp ''; installPhase = '' mkdir -p $out/share cp -r . $out/share/gitlab ln -sf ${assets} $out/share/gitlab/public/assets rm -rf $out/share/gitlab/log ln -sf /run/gitlab/log $out/share/gitlab/log ln -sf /run/gitlab/uploads $out/share/gitlab/public/uploads ln -sf /run/gitlab/config $out/share/gitlab/config ln -sf /run/gitlab/tmp $out/share/gitlab/tmp # rake tasks to mitigate CVE-2017-0882 # see https://about.gitlab.com/2017/03/20/gitlab-8-dot-17-dot-4-security-release/ cp ${./reset_token.rake} $out/share/gitlab/lib/tasks/reset_token.rake # manually patch the shebang line in generate-loose-foreign-key wrapProgram $out/share/gitlab/scripts/decomposition/generate-loose-foreign-key --set ENABLE_SPRING 0 --add-flags 'runner -e test' ''; passthru = { inherit rubyEnv assets; ruby = rubyEnv.wrappedRuby; GITALY_SERVER_VERSION = data.passthru.GITALY_SERVER_VERSION; GITLAB_PAGES_VERSION = data.passthru.GITLAB_PAGES_VERSION; GITLAB_SHELL_VERSION = data.passthru.GITLAB_SHELL_VERSION; GITLAB_WORKHORSE_VERSION = data.passthru.GITLAB_WORKHORSE_VERSION; gitlabEnv.FOSS_ONLY = lib.boolToString (!gitlabEnterprise); tests = { nixos-test-passes = nixosTests.gitlab; }; }; meta = { homepage = "http://www.gitlab.com/"; platforms = lib.platforms.linux; teams = [ lib.teams.gitlab ]; } // ( if gitlabEnterprise then { license = lib.licenses.unfreeRedistributable; # https://gitlab.com/gitlab-org/gitlab-ee/raw/master/LICENSE description = "GitLab Enterprise Edition"; } else { license = lib.licenses.mit; description = "GitLab Community Edition"; longDescription = "GitLab Community Edition (CE) is an open source end-to-end software development platform with built-in version control, issue tracking, code review, CI/CD, and more. Self-host GitLab CE on your own servers, in a container, or on a cloud provider."; } ); }