push sheeet
Some checks failed
Periodic Merges (6h) / master → staging-nixos (push) Failing after 12m50s
Periodic Merges (6h) / master → staging-next (push) Failing after 12m54s
Periodic Merges (24h) / merge-base(master,staging) → haskell-updates (push) Failing after 11m54s
Periodic Merges (6h) / staging-next → staging (push) Failing after 12m13s
Periodic Merges (24h) / staging-next-25.05 → staging-25.05 (push) Failing after 13m24s
Periodic Merges (24h) / release-25.05 → staging-next-25.05 (push) Failing after 14m28s

This commit is contained in:
Dark Steveneq
2025-10-09 14:15:47 +02:00
commit 646b892680
49168 changed files with 5897842 additions and 0 deletions

View File

@@ -0,0 +1,256 @@
{
stdenv,
lib,
buildPackages,
runCommand,
ruby,
defaultGemConfig,
buildRubyGem,
buildEnv,
makeBinaryWrapper,
bundler,
}@defs:
{
name ? null,
pname ? null,
version ? null,
mainGemName ? null,
gemdir ? null,
gemfile ? null,
lockfile ? null,
gemset ? null,
ruby ? defs.ruby,
copyGemFiles ? false, # Copy gem files instead of symlinking
gemConfig ? defaultGemConfig,
postBuild ? null,
document ? [ ],
meta ? { },
groups ? null,
ignoreCollisions ? false,
nativeBuildInputs ? [ ],
buildInputs ? [ ],
extraConfigPaths ? [ ],
passthru ? { },
...
}@args:
assert name == null -> pname != null;
let
functions = import ./functions.nix { inherit lib gemConfig; };
inherit (functions)
applyGemConfigs
bundlerFiles
composeGemAttrs
filterGemset
genStubsScript
pathDerivation
;
gemFiles = bundlerFiles args;
importedGemset =
if builtins.typeOf gemFiles.gemset != "set" then import gemFiles.gemset else gemFiles.gemset;
filteredGemset = filterGemset { inherit ruby groups; } importedGemset;
configuredGemset = lib.flip lib.mapAttrs filteredGemset (
name: attrs:
applyGemConfigs (
attrs
// {
inherit ruby document;
gemName = name;
}
)
);
hasBundler = builtins.hasAttr "bundler" filteredGemset;
bundler =
if hasBundler then
gems.bundler
else
defs.bundler.override (attrs: {
inherit ruby;
});
gems = lib.flip lib.mapAttrs configuredGemset (name: attrs: buildGem name attrs);
version' =
if version != null then
version
else if pname != null then
gems.${pname}.suffix
else
null;
name' = if name != null then name else "${pname}-${version'}";
pname' = if pname != null then pname else name;
copyIfBundledByPath =
{
bundledByPath ? false,
...
}:
(lib.optionalString bundledByPath (
assert gemFiles.gemdir != null;
"cp -a ${gemFiles.gemdir}/* $out/"
) # */
);
maybeCopyAll =
pkgname:
lib.optionalString (pkgname != null) (
let
mainGem = gems.${pkgname} or (throw "bundlerEnv: gem ${pkgname} not found");
in
copyIfBundledByPath mainGem
);
# We have to normalize the Gemfile.lock, otherwise bundler tries to be
# helpful by doing so at run time, causing executables to immediately bail
# out. Yes, I'm serious.
confFiles = runCommand "gemfile-and-lockfile" { } ''
mkdir -p $out
${maybeCopyAll mainGemName}
cp ${gemFiles.gemfile} $out/Gemfile || ls -l $out/Gemfile
cp ${gemFiles.lockfile} $out/Gemfile.lock || ls -l $out/Gemfile.lock
${lib.concatMapStringsSep "\n" (path: "cp -r ${path} $out/") extraConfigPaths}
'';
buildGem =
name: attrs:
(
let
gemAttrs = composeGemAttrs ruby gems name attrs;
in
if gemAttrs.type == "path" then
pathDerivation (gemAttrs.source // gemAttrs)
else
buildRubyGem gemAttrs
);
envPaths = lib.attrValues gems ++ lib.optional (!hasBundler) bundler;
basicEnvArgs = {
inherit
nativeBuildInputs
buildInputs
ignoreCollisions
pname
;
name = name';
version = version';
paths = envPaths;
pathsToLink = [ "/lib" ];
postBuild =
genStubsScript (
defs
// args
// {
inherit confFiles bundler groups;
binPaths = envPaths;
}
)
+ lib.optionalString (postBuild != null) postBuild;
meta = {
platforms = ruby.meta.platforms;
}
// meta;
passthru = (
lib.optionalAttrs (pname != null) {
inherit (gems.${pname}) gemType;
}
// rec {
inherit
ruby
bundler
gems
confFiles
envPaths
;
wrappedRuby = stdenv.mkDerivation {
name = "wrapped-ruby-${pname'}";
nativeBuildInputs = [ makeBinaryWrapper ];
dontUnpack = true;
buildPhase = ''
mkdir -p $out/bin
for i in ${ruby}/bin/*; do
makeWrapper "$i" $out/bin/$(basename "$i") \
--set BUNDLE_GEMFILE ${confFiles}/Gemfile \
--unset BUNDLE_PATH \
--set BUNDLE_FROZEN 1 \
--set GEM_HOME ${basicEnv}/${ruby.gemPath} \
--set GEM_PATH ${basicEnv}/${ruby.gemPath}
done
'';
dontInstall = true;
doCheck = true;
checkPhase = ''
$out/bin/ruby --help > /dev/null
'';
inherit (ruby) meta;
};
env =
let
irbrc = builtins.toFile "irbrc" ''
if !(ENV["OLD_IRBRC"].nil? || ENV["OLD_IRBRC"].empty?)
require ENV["OLD_IRBRC"]
end
require 'rubygems'
require 'bundler/setup'
'';
in
stdenv.mkDerivation {
name = "${pname'}-interactive-environment";
nativeBuildInputs = [
wrappedRuby
basicEnv
];
shellHook = ''
export OLD_IRBRC=$IRBRC
export IRBRC=${irbrc}
'';
buildCommand = ''
echo >&2 ""
echo >&2 "*** Ruby 'env' attributes are intended for interactive nix-shell sessions, not for building! ***"
echo >&2 ""
exit 1
'';
};
}
// passthru
);
};
basicEnv =
if copyGemFiles then
runCommand name' basicEnvArgs ''
mkdir -p $out
for i in $paths; do
${buildPackages.rsync}/bin/rsync -a $i/lib $out/
done
eval "$postBuild"
''
else
buildEnv basicEnvArgs;
in
basicEnv

View File

@@ -0,0 +1,155 @@
{ lib, gemConfig, ... }:
let
inherit (lib)
attrValues
concatMap
converge
filterAttrs
getAttrs
intersectLists
;
in
rec {
bundlerFiles =
{
gemfile ? null,
lockfile ? null,
gemset ? null,
gemdir ? null,
...
}:
{
inherit gemdir;
gemfile =
if gemfile == null then
assert gemdir != null;
gemdir + "/Gemfile"
else
gemfile;
lockfile =
if lockfile == null then
assert gemdir != null;
gemdir + "/Gemfile.lock"
else
lockfile;
gemset =
if gemset == null then
assert gemdir != null;
gemdir + "/gemset.nix"
else
gemset;
};
filterGemset =
{ ruby, groups, ... }:
gemset:
let
platformGems = filterAttrs (_: platformMatches ruby) gemset;
directlyMatchingGems = filterAttrs (_: groupMatches groups) platformGems;
expandDependencies =
gems:
let
depNames = concatMap (gem: gem.dependencies or [ ]) (attrValues gems);
deps = getAttrs depNames platformGems;
in
gems // deps;
in
converge expandDependencies directlyMatchingGems;
platformMatches =
{ rubyEngine, version, ... }:
attrs:
(
!(attrs ? platforms)
|| builtins.length attrs.platforms == 0
|| builtins.any (
platform:
platform.engine == rubyEngine && (!(platform ? version) || platform.version == version.majMin)
) attrs.platforms
);
groupMatches =
groups: attrs:
groups == null
|| !(attrs ? groups)
|| (intersectLists (groups ++ [ "default" ]) attrs.groups) != [ ];
applyGemConfigs =
attrs: (if gemConfig ? ${attrs.gemName} then attrs // gemConfig.${attrs.gemName} attrs else attrs);
genStubsScript =
{
lib,
runCommand,
ruby,
confFiles,
bundler,
groups,
binPaths,
...
}:
let
genStubsScript =
runCommand "gen-bin-stubs"
{
strictDeps = true;
nativeBuildInputs = [ ruby ];
}
''
cp ${./gen-bin-stubs.rb} $out
chmod +x $out
patchShebangs --build $out
'';
in
''
${genStubsScript} \
"${ruby}/bin/ruby" \
"${confFiles}/Gemfile" \
"$out/${ruby.gemPath}" \
"${bundler}/${ruby.gemPath}/gems/bundler-${bundler.version}" \
${lib.escapeShellArg binPaths} \
${lib.escapeShellArg groups}
'';
pathDerivation =
{
gemName,
version,
path,
...
}:
let
res = {
type = "derivation";
bundledByPath = true;
name = gemName;
version = version;
outPath = "${path}";
outputs = [ "out" ];
out = res;
outputName = "out";
suffix = version;
gemType = "path";
};
in
res;
composeGemAttrs =
ruby: gems: name: attrs:
(
(removeAttrs attrs [ "platforms" ])
// {
inherit ruby;
inherit (attrs.source) type;
source = removeAttrs attrs.source [ "type" ];
gemName = name;
gemPath = map (gemName: gems.${gemName}) (attrs.dependencies or [ ]);
}
);
}

View File

@@ -0,0 +1,66 @@
#!/usr/bin/env ruby
require 'rbconfig'
require 'rubygems'
require 'rubygems/specification'
require 'fileutils'
# args/settings
out = ENV["out"]
ruby = ARGV[0]
gemfile = ARGV[1]
bundle_path = ARGV[2]
bundler_path = ARGV[3]
paths = ARGV[4].split
groups = ARGV[5].split
# generate binstubs
FileUtils.mkdir_p("#{out}/bin")
paths.each do |path|
next unless File.directory?("#{path}/nix-support/gem-meta")
name = File.read("#{path}/nix-support/gem-meta/name")
executables = File.read("#{path}/nix-support/gem-meta/executables")
.force_encoding('UTF-8').split
executables.each do |exe|
File.open("#{out}/bin/#{exe}", "w") do |f|
f.write(<<-EOF)
#!#{ruby}
#
# This file was generated by Nix.
#
# The application '#{exe}' is installed as part of a gem, and
# this file is here to facilitate running it.
#
ENV["BUNDLE_GEMFILE"] = #{gemfile.dump}
ENV.delete 'BUNDLE_PATH'
ENV['BUNDLE_FROZEN'] = '1'
ENV['BUNDLE_IGNORE_CONFIG'] = '1'
Gem.paths = { 'GEM_HOME' => #{bundle_path.dump} }
$LOAD_PATH.unshift #{File.join(bundler_path, "/lib").dump}
require 'bundler'
# Monkey-patch out the check that Bundler performs to determine
# whether the bundler env is writable. It's not writable, even for
# root! And for this use of Bundler, it shouldn't be necessary since
# we're not trying to perform any package management operations, only
# produce a Gem path. Thus, we replace it with a method that will
# always return false, to squelch a warning from Bundler saying that
# sudo may be required.
module Bundler
class <<self
def requires_sudo?
return false
end
end
end
Bundler.setup(#{groups.map(&:dump).join(', ')})
load Gem.bin_path(#{name.dump}, #{exe.dump})
EOF
FileUtils.chmod("+x", "#{out}/bin/#{exe}")
end
end
end

View File

@@ -0,0 +1,140 @@
{
lib,
ruby,
defaultGemConfig,
test,
should,
}:
let
testConfigs = {
inherit lib;
gemConfig = defaultGemConfig;
};
functions = (import ./functions.nix testConfigs);
in
builtins.concatLists [
(test.run "All set, no gemdir"
(functions.bundlerFiles {
gemfile = test/Gemfile;
lockfile = test/Gemfile.lock;
gemset = test/gemset.nix;
})
{
gemfile = should.equal test/Gemfile;
lockfile = should.equal test/Gemfile.lock;
gemset = should.equal test/gemset.nix;
}
)
(test.run "Just gemdir"
(functions.bundlerFiles {
gemdir = test/.;
})
{
gemfile = should.equal test/Gemfile;
lockfile = should.equal test/Gemfile.lock;
gemset = should.equal test/gemset.nix;
}
)
(test.run "Gemset and dir"
(functions.bundlerFiles {
gemdir = test/.;
gemset = test/extraGemset.nix;
})
{
gemfile = should.equal test/Gemfile;
lockfile = should.equal test/Gemfile.lock;
gemset = should.equal test/extraGemset.nix;
}
)
(test.run "Filter empty gemset" { } (
set:
functions.filterGemset {
inherit ruby;
groups = [ "default" ];
} set == { }
))
(
let
gemSet = {
test = {
groups = [
"x"
"y"
];
};
};
in
test.run "Filter matches a group" gemSet (
set:
functions.filterGemset {
inherit ruby;
groups = [
"y"
"z"
];
} set == gemSet
)
)
(
let
gemSet = {
test = {
platforms = [ ];
};
};
in
test.run "Filter matches empty platforms list" gemSet (
set:
functions.filterGemset {
inherit ruby;
groups = [ ];
} set == gemSet
)
)
(
let
gemSet = {
test = {
platforms = [
{
engine = ruby.rubyEngine;
version = ruby.version.majMin;
}
];
};
};
in
test.run "Filter matches on platform" gemSet (
set:
functions.filterGemset {
inherit ruby;
groups = [ ];
} set == gemSet
)
)
(
let
gemSet = {
test = {
groups = [
"x"
"y"
];
};
};
in
test.run "Filter excludes based on groups" gemSet (
set:
functions.filterGemset {
inherit ruby;
groups = [
"a"
"b"
];
} set == { }
)
)
]