Loading maintainers/scripts/update-ruby-packages +55 −12 Original line number Diff line number Diff line #!/usr/bin/env nix-shell #!nix-shell -i bash -p bundler bundix #!nix-shell -i bash -p bundler bundix nixfmt # shellcheck shell=bash set -euf -o pipefail ( self="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")")" getSpecifiedGems() { grep '^\s*gem' "$1" | cut -d"'" -f2 } cd pkgs/development/ruby-modules/with-packages # Cleanup possible leftovers from a failed run. rm -f gemset.nix Gemfile.lock # Since bundler 2+, the lock command generates a platform-dependent # Gemfile.lock, hence causing to bundix to generate a gemset tied to the # platform from where it was executed. BUNDLE_FORCE_RUBY_PLATFORM=1 bundle lock bundix mv gemset.nix ../../../top-level/ruby-packages.nix rm -f Gemfile.lock ) nixfmt gemset.nix # Run checks against the update. if ! \ nix-instantiate --eval --strict \ --argstr specifiedGems "$(getSpecifiedGems Gemfile)"\ --arg old ../../../top-level/ruby-packages.nix \ --arg new ./gemset.nix \ "$self/update-ruby-packages.checks.nix" then ( echo "" echo "NOTE: The Gemfile.lock and gemset.nix files were left intact for comparison." echo "" echo "Do not simply continue through with the update." echo "Make sure to get the Ruby maintainers involved in finding a solution to this problem." echo "" echo "The non-specified gems listed are generally not at fault." echo "Regressions likely come from specified gems getting updated and having clashing requirements." echo "" echo "Start by pessimistically pinning (~>) specified gems to their current full version that look like they could be the cause." echo "Then once only non-specified gems are regressed, pessimistically pin the leftover ones." echo "Once this passes with pessimistic pinning of gems, try reducing specificity in pessimistic bounds, then try using minimum version bounds (>=)." echo "At some point bundler will tell you why it can't give you the bounds being asked for." echo "" echo "Don't forget to re-generate the ruby-packages.nix nix from scratch for the proper report once the minimum required set of pins is known!" echo "" ) >&2 exit 1 fi { echo "# This file is generated and should be updated with maintainers/scripts/update-ruby-packages." echo "" cat gemset.nix } > ../../../top-level/ruby-packages.nix rm -v -f gemset.nix Gemfile.lock maintainers/scripts/update-ruby-packages.checks.nix 0 → 100644 +210 −0 Original line number Diff line number Diff line { old, new, specifiedGems, withData ? false, # Use for `lib`. pkgs ? import ../.. { }, }: # Rename inputs to re-use those names. let old' = old; new' = new; specifiedGems' = specifiedGems; in let inherit (builtins) attrNames concatStrings filter genList isNull length stringLength toJSON ; inherit (pkgs.lib) concatMapStringsSep concatStringsSep intersectLists splitString subtractLists versionOlder ; # Keeps non-nulls in a list. # Mirroring Ruby's `Array#compact`. compact = filter (v: !(isNull v)); # The full gemsets attribute sets. old = import old'; new = import new'; # All gem names. allGems = attrNames (old // new); # Gems found in both old and new. keptGems = intersectLists (attrNames old) (attrNames new); # Gems added or removed. addedOrRemovedGems = subtractLists keptGems allGems; # Gems specified in Gemfile. specifiedGems = splitString "\n" specifiedGems'; # Gems that were not specified. nonSpecifiedGems = subtractLists specifiedGems keptGems; # Generates data for the summary tables # This is also used for `failedChecks`. versionChangeDataFor = gems: let results = map ( name: let oldv = old.${name}.version or null; newv = new.${name}.version or null; in if newv == oldv then # Nothing changed. This will be filtered out. null else { inherit name ; old = oldv; new = newv; } ) gems; in compact results; checkRegression = entry: message: let isRemoval = isNull entry.new; isAddition = isNull entry.old; isRegression = versionOlder entry.new entry.old; in if # Gems being added or gems being removed won't cause failures. !isRemoval && !isAddition # A version being regressed is a failure. && isRegression then message else null; # This is a list of error messages to float up to the user. # An empty list means no error. failedChecks = compact ( [ ] ++ (map ( entry: checkRegression entry "Version regression for specified gem ${toJSON entry.name}, from ${toJSON entry.old} to ${toJSON entry.new}" ) (versionChangeDataFor specifiedGems)) ++ (map ( entry: checkRegression entry "Version regression for non-specified gem ${toJSON entry.name}, from ${toJSON entry.old} to ${toJSON entry.new}" ) (versionChangeDataFor nonSpecifiedGems)) ); # Formats a version number (or null) as markdown. gemVersionToMD = version: if isNull version then "*N/A*" else "`${version}`"; # Formats a `versionChangeDataFor` output as markdown. versionChangeDataMD = gems: let result = versionChangeDataFor gems; in map ( row: [ row.name ] ++ (map gemVersionToMD [ row.old row.new ]) ) result; # Given a list of columns, and a list of list of column data, # generates the markup for markdown table. mkTable = columns: entries: let entryToMarkdown = columns: "| ${concatStringsSep " | " columns} |"; sep = entryToMarkdown (map (_: "---") columns); in if length entries == 0 then "> *No data...*" else '' ${entryToMarkdown columns} ${sep} ${concatMapStringsSep "\n" entryToMarkdown entries} ''; # The markdown report is built as this string. report = '' <!-- ---------------------------------------------- NOTE: You must copy this whole report section to your pull request! ---------------------------------------------- --> #### Nixpkgs Ruby packages update report **Specified gems changed:** ${mkTable [ "Name" "old" "new" ] (versionChangeDataMD specifiedGems)} **Gems added or removed:** ${mkTable [ "Name" "old" "new" ] (versionChangeDataMD addedOrRemovedGems)} <details> <summary><strong>(Non-specified gem changes)</strong></summary> ${mkTable [ "Name" "old" "new" ] (versionChangeDataMD nonSpecifiedGems)} </details> <!-- --------------- End ----------------- --> ''; in if (length failedChecks) > 0 then # Fail the update script via `abort` on checks failure. builtins.abort '' ${"\n"}Gem upgrade aborted with the following failures: ${concatMapStringsSep "\n" (msg: " - ${msg}") failedChecks} '' else # Output the report. builtins.trace "(Report follows...)\n\n${report}" ( # And if `withData` is true, expose the data for REPL usage. if withData then { inherit # The gemsets used old new # The lists of gems allGems specifiedGems nonSpecifiedGems addedOrRemovedGems keptGems ; } else null ) pkgs/development/ruby-modules/with-packages/Gemfile +26 −5 Original line number Diff line number Diff line Loading @@ -9,7 +9,12 @@ gem 'bacon' gem 'byebug' gem 'cairo' gem 'cairo-gobject' gem 'camping', '~> 3' # Because camping >= 3.0.0 depends on rackup ~> 2.1.0 # and Gemfile depends on camping ~> 3.1.0, # rackup ~> 2.1.0 is required. # So, because Gemfile depends on rackup ~> 2.2.1, # version solving has failed. gem 'camping', '~> 2.1.532' # gem 'capybara-webkit' takes too long to build right now since webkit depends on ruby gem 'charlock_holmes' gem 'cf-uaac' Loading Loading @@ -51,7 +56,12 @@ gem 'dip' gem 'domain_name' gem 'do_sqlite3' gem 'erb-formatter' gem 'ethon' # Because typhoeus >= 1.5.0 depends on ethon >= 0.9.0, < 0.16.0 # and Gemfile depends on ethon ~> 0.16.0, # typhoeus >= 1.5.0 cannot be used. # So, because Gemfile depends on typhoeus ~> 1.5.0, # version solving has failed. gem 'ethon', '~> 0.16.0' gem 'eventmachine' gem 'excon' gem 'faraday' Loading Loading @@ -115,7 +125,18 @@ gem 'pry-doc' gem 'public_suffix' gem 'puma' gem 'pwntools' gem 'rails', '~> 8' # And because rails >= 8.1.1 depends on activesupport = 8.1.1 # and jekyll-webmention_io >= 4.0.0 depends on activesupport >= 7.0.4.3, < 8.A, # jekyll-webmention_io >= 4.0.0 is incompatible with rails >= 8.0.0. # So, because Gemfile depends on jekyll-webmention_io ~> 4.1.0 # and Gemfile depends on rails ~> 8, # version solving has failed. # ... # Thus, cocoapods >= 1.16.2 is incompatible with rails >= 8.0.0. # So, because Gemfile depends on cocoapods ~> 1.16.2 # and Gemfile depends on rails ~> 8, # version solving has failed. gem 'rails', '~> 7.2' gem 'rainbow' # gem 'rbczmq' deprecated gem 'rbnacl' Loading @@ -135,7 +156,7 @@ gem 'ruby-lxc' gem 'ruby-progressbar' gem 'ruby-terminfo' gem 'ruby-vips' gem 'rubyzip', '~>3' gem 'rubyzip' gem 'rugged' gem 'sassc' gem 'scrypt' Loading @@ -143,11 +164,11 @@ gem 'seccomp-tools' gem 'semian' gem 'sequel' gem 'sequel_pg' gem 'solargraph' gem 'simplecov' gem 'sinatra' gem 'slop' gem 'snappy' gem 'solargraph' gem 'snmp' gem 'sqlite3' gem 'standard' Loading pkgs/top-level/ruby-packages.nix +1228 −308 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
maintainers/scripts/update-ruby-packages +55 −12 Original line number Diff line number Diff line #!/usr/bin/env nix-shell #!nix-shell -i bash -p bundler bundix #!nix-shell -i bash -p bundler bundix nixfmt # shellcheck shell=bash set -euf -o pipefail ( self="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")")" getSpecifiedGems() { grep '^\s*gem' "$1" | cut -d"'" -f2 } cd pkgs/development/ruby-modules/with-packages # Cleanup possible leftovers from a failed run. rm -f gemset.nix Gemfile.lock # Since bundler 2+, the lock command generates a platform-dependent # Gemfile.lock, hence causing to bundix to generate a gemset tied to the # platform from where it was executed. BUNDLE_FORCE_RUBY_PLATFORM=1 bundle lock bundix mv gemset.nix ../../../top-level/ruby-packages.nix rm -f Gemfile.lock ) nixfmt gemset.nix # Run checks against the update. if ! \ nix-instantiate --eval --strict \ --argstr specifiedGems "$(getSpecifiedGems Gemfile)"\ --arg old ../../../top-level/ruby-packages.nix \ --arg new ./gemset.nix \ "$self/update-ruby-packages.checks.nix" then ( echo "" echo "NOTE: The Gemfile.lock and gemset.nix files were left intact for comparison." echo "" echo "Do not simply continue through with the update." echo "Make sure to get the Ruby maintainers involved in finding a solution to this problem." echo "" echo "The non-specified gems listed are generally not at fault." echo "Regressions likely come from specified gems getting updated and having clashing requirements." echo "" echo "Start by pessimistically pinning (~>) specified gems to their current full version that look like they could be the cause." echo "Then once only non-specified gems are regressed, pessimistically pin the leftover ones." echo "Once this passes with pessimistic pinning of gems, try reducing specificity in pessimistic bounds, then try using minimum version bounds (>=)." echo "At some point bundler will tell you why it can't give you the bounds being asked for." echo "" echo "Don't forget to re-generate the ruby-packages.nix nix from scratch for the proper report once the minimum required set of pins is known!" echo "" ) >&2 exit 1 fi { echo "# This file is generated and should be updated with maintainers/scripts/update-ruby-packages." echo "" cat gemset.nix } > ../../../top-level/ruby-packages.nix rm -v -f gemset.nix Gemfile.lock
maintainers/scripts/update-ruby-packages.checks.nix 0 → 100644 +210 −0 Original line number Diff line number Diff line { old, new, specifiedGems, withData ? false, # Use for `lib`. pkgs ? import ../.. { }, }: # Rename inputs to re-use those names. let old' = old; new' = new; specifiedGems' = specifiedGems; in let inherit (builtins) attrNames concatStrings filter genList isNull length stringLength toJSON ; inherit (pkgs.lib) concatMapStringsSep concatStringsSep intersectLists splitString subtractLists versionOlder ; # Keeps non-nulls in a list. # Mirroring Ruby's `Array#compact`. compact = filter (v: !(isNull v)); # The full gemsets attribute sets. old = import old'; new = import new'; # All gem names. allGems = attrNames (old // new); # Gems found in both old and new. keptGems = intersectLists (attrNames old) (attrNames new); # Gems added or removed. addedOrRemovedGems = subtractLists keptGems allGems; # Gems specified in Gemfile. specifiedGems = splitString "\n" specifiedGems'; # Gems that were not specified. nonSpecifiedGems = subtractLists specifiedGems keptGems; # Generates data for the summary tables # This is also used for `failedChecks`. versionChangeDataFor = gems: let results = map ( name: let oldv = old.${name}.version or null; newv = new.${name}.version or null; in if newv == oldv then # Nothing changed. This will be filtered out. null else { inherit name ; old = oldv; new = newv; } ) gems; in compact results; checkRegression = entry: message: let isRemoval = isNull entry.new; isAddition = isNull entry.old; isRegression = versionOlder entry.new entry.old; in if # Gems being added or gems being removed won't cause failures. !isRemoval && !isAddition # A version being regressed is a failure. && isRegression then message else null; # This is a list of error messages to float up to the user. # An empty list means no error. failedChecks = compact ( [ ] ++ (map ( entry: checkRegression entry "Version regression for specified gem ${toJSON entry.name}, from ${toJSON entry.old} to ${toJSON entry.new}" ) (versionChangeDataFor specifiedGems)) ++ (map ( entry: checkRegression entry "Version regression for non-specified gem ${toJSON entry.name}, from ${toJSON entry.old} to ${toJSON entry.new}" ) (versionChangeDataFor nonSpecifiedGems)) ); # Formats a version number (or null) as markdown. gemVersionToMD = version: if isNull version then "*N/A*" else "`${version}`"; # Formats a `versionChangeDataFor` output as markdown. versionChangeDataMD = gems: let result = versionChangeDataFor gems; in map ( row: [ row.name ] ++ (map gemVersionToMD [ row.old row.new ]) ) result; # Given a list of columns, and a list of list of column data, # generates the markup for markdown table. mkTable = columns: entries: let entryToMarkdown = columns: "| ${concatStringsSep " | " columns} |"; sep = entryToMarkdown (map (_: "---") columns); in if length entries == 0 then "> *No data...*" else '' ${entryToMarkdown columns} ${sep} ${concatMapStringsSep "\n" entryToMarkdown entries} ''; # The markdown report is built as this string. report = '' <!-- ---------------------------------------------- NOTE: You must copy this whole report section to your pull request! ---------------------------------------------- --> #### Nixpkgs Ruby packages update report **Specified gems changed:** ${mkTable [ "Name" "old" "new" ] (versionChangeDataMD specifiedGems)} **Gems added or removed:** ${mkTable [ "Name" "old" "new" ] (versionChangeDataMD addedOrRemovedGems)} <details> <summary><strong>(Non-specified gem changes)</strong></summary> ${mkTable [ "Name" "old" "new" ] (versionChangeDataMD nonSpecifiedGems)} </details> <!-- --------------- End ----------------- --> ''; in if (length failedChecks) > 0 then # Fail the update script via `abort` on checks failure. builtins.abort '' ${"\n"}Gem upgrade aborted with the following failures: ${concatMapStringsSep "\n" (msg: " - ${msg}") failedChecks} '' else # Output the report. builtins.trace "(Report follows...)\n\n${report}" ( # And if `withData` is true, expose the data for REPL usage. if withData then { inherit # The gemsets used old new # The lists of gems allGems specifiedGems nonSpecifiedGems addedOrRemovedGems keptGems ; } else null )
pkgs/development/ruby-modules/with-packages/Gemfile +26 −5 Original line number Diff line number Diff line Loading @@ -9,7 +9,12 @@ gem 'bacon' gem 'byebug' gem 'cairo' gem 'cairo-gobject' gem 'camping', '~> 3' # Because camping >= 3.0.0 depends on rackup ~> 2.1.0 # and Gemfile depends on camping ~> 3.1.0, # rackup ~> 2.1.0 is required. # So, because Gemfile depends on rackup ~> 2.2.1, # version solving has failed. gem 'camping', '~> 2.1.532' # gem 'capybara-webkit' takes too long to build right now since webkit depends on ruby gem 'charlock_holmes' gem 'cf-uaac' Loading Loading @@ -51,7 +56,12 @@ gem 'dip' gem 'domain_name' gem 'do_sqlite3' gem 'erb-formatter' gem 'ethon' # Because typhoeus >= 1.5.0 depends on ethon >= 0.9.0, < 0.16.0 # and Gemfile depends on ethon ~> 0.16.0, # typhoeus >= 1.5.0 cannot be used. # So, because Gemfile depends on typhoeus ~> 1.5.0, # version solving has failed. gem 'ethon', '~> 0.16.0' gem 'eventmachine' gem 'excon' gem 'faraday' Loading Loading @@ -115,7 +125,18 @@ gem 'pry-doc' gem 'public_suffix' gem 'puma' gem 'pwntools' gem 'rails', '~> 8' # And because rails >= 8.1.1 depends on activesupport = 8.1.1 # and jekyll-webmention_io >= 4.0.0 depends on activesupport >= 7.0.4.3, < 8.A, # jekyll-webmention_io >= 4.0.0 is incompatible with rails >= 8.0.0. # So, because Gemfile depends on jekyll-webmention_io ~> 4.1.0 # and Gemfile depends on rails ~> 8, # version solving has failed. # ... # Thus, cocoapods >= 1.16.2 is incompatible with rails >= 8.0.0. # So, because Gemfile depends on cocoapods ~> 1.16.2 # and Gemfile depends on rails ~> 8, # version solving has failed. gem 'rails', '~> 7.2' gem 'rainbow' # gem 'rbczmq' deprecated gem 'rbnacl' Loading @@ -135,7 +156,7 @@ gem 'ruby-lxc' gem 'ruby-progressbar' gem 'ruby-terminfo' gem 'ruby-vips' gem 'rubyzip', '~>3' gem 'rubyzip' gem 'rugged' gem 'sassc' gem 'scrypt' Loading @@ -143,11 +164,11 @@ gem 'seccomp-tools' gem 'semian' gem 'sequel' gem 'sequel_pg' gem 'solargraph' gem 'simplecov' gem 'sinatra' gem 'slop' gem 'snappy' gem 'solargraph' gem 'snmp' gem 'sqlite3' gem 'standard' Loading
pkgs/top-level/ruby-packages.nix +1228 −308 File changed.Preview size limit exceeded, changes collapsed. Show changes