Removing puppet-modules-gems
What are puppet-module-gems
and why are they now considered harmful?
Okay, that might be a bit of a melodramatic take on the situation, but the puppet-module-gems
really did serve a useful purpose at one point. Let’s go back over why it exists and what’s changed in the meantime.
We’ll start with Bundler–a local environment manager for Ruby projects. It runs a Ruby project with all of its required dependencies in a sandbox, meaning that you don’t need to install gems globally. This is great for repeatability and means that each project can have the specific gem versions it requires without interfering with other projects that might need different versions.
This worked great for Puppet back in the good old days when things were simple.
All the gems Puppet used were specified in the Gemfile
and Bundler environments just worked.
But then the Ruby ecosystem evolved and gems started taking advantage of new functionality.
Version 1.2 of a gem might run on Ruby 1.8, but an upgrade to version 1.4 was required to use Ruby 2.0 and then Ruby 2.5 needed yet another version.
This got to be rather complex, and worse Bundler couldn’t resolve these dependencies properly! We had to do it ourselves, making the Gemfiles
brittle and harder to read and maintain.
CI was breaking, module authors were struggling to test on multiple platforms, users were having a hard time validating updates.
Clearly we needed a solution, and the puppet-module-gems
were the answer.
We created meta-gems for each Ruby version we supported and specified the proper dependencies in these gems.
Then the Gemfile
would conditionally depend on the properly versioned meta-gem based on the version of Ruby running.
This worked reasonably well, but had its challenges.
The dependencies were opaque and the extra level of abstraction meant that looking at the Gemfile
didn’t really tell you much about what gems would be installed or used.
Worse, we had to manually resolve and specify all the dependencies for every Ruby version, and keeping up with those turned out to be easier said than done.
We often fell behind and our more adventurous users would regularly remind us of that.
The situation certainly wasn’t ideal, but it was a workable compromise.
Until Bundler got smarter, learned how to resolve Ruby version dependencies, and made this workaround no longer necessary.
What’s Changing
With these Bundler updates, we no longer require such tight control over dependencies; Bundler will do the right thing and resolve them properly, taking gems' supported Ruby versions into account. We have decided to stop maintaining the puppet-module-gems
and instead move fully to managing gem dependencies with each module’s Gemfile
. This is standard practice for the Ruby ecosystem, so it will be easier to maintain in the long term. Since it’s more flexible, it will also better allow downstream users to modify according to their own needs.
This is especially relevant now as it means that the puppet-modules-gems
have not been updated to account for Ruby 3, which the upcoming Puppet 8 release will release with. This means that any module that has not yet converted to the new format will be not be testable on Puppet 8.
What You Need To Do
If you maintain your own Puppet modules, the rest of this post is for you. If you only use modules from the Forge, then no action is necessary on your part, other than to file bug reports with a link to this blog post if you notice modules which should be updated.
Thankfully, the solution to adopting the new standard is relatively simple, at least for those who are using the PDK and the default templates. We’ve already made the updates for you and you’ll just need to update your module with these changes. This will replace puppet-module-gems
in the Gemfile
with the actual dependencies that the meta-gem represented.
$ pdk update
An example of the changes this update includes can be seen on many of our modules, with one example shown here.
In the event that you are not using the default PDK templates then you will need to update each Gemfile
yourself. Look at the config default values here and copy what you need into your own template repository, updating them according to your own needs.
If you are not using the PDK at all, you can find an example of a current Gemfile
below, which you can copy and modify as you need. Tracking the default versions listed under the Gemfile
key in config_defaults.yml will help you keep these versions correct as you update over time.
group :development do
# These where already set in pdk templates and so may not be relevant to you
gem "json", '~> 2.0', require: false
gem "voxpupuli-puppet-lint-plugins", '~> 3.0', require: false
# The main group of rules we are adding
gem "facterdb", '~> 1.18', require: false
gem "metadata-json-lint", '>= 2.0.2', '< 4.0.0', require: false
gem "puppetlabs_spec_helper", '>= 2.9.0', '< 4.0.0', require: false
gem "rspec-puppet-facts", '>= 1.10.0', '< 3.0.0', require: false
gem "codecov", '~> 0.2', require: false
gem "dependency_checker", '~> 0.2', require: false
gem "parallel_tests", '~> 3.4', require: false
gem "pry", '~> 0.10', require: false
gem "simplecov-console", '~> 0.5', require: false
gem "puppet-debugger", '~> 1.0', require: false
## We pin these in order to keep the style rules static
gem "rubocop", '= 1.6.1', require: false
gem "rubocop-performance", '= 1.9.1', require: false
gem "rubocop-rspec", '= 2.0.1', require: false
gem "rb-readline", '= 0.5.5', require: false, platforms: [:mswin, :mingw, :x64_mingw]
# Added locally and used by us as part of the release process, may not be relevant to you
gem "github_changelog_generator", require: false
end
group :system_tests do
gem "puppet_litmus", '< 1.0.0', require: false, platforms: [:ruby]
gem "serverspec", '~> 2.41', require: false
end
Conclusions
Since we are not un-publishing the existing puppet-module-gems
meta-gems, we don’t expect many people to be impacted by this change.
Many module authors won’t even notice it, as they’ll pdk update
their modules naturally at some point and inherit the updates before it becomes critical for them.
What this does mean, though, is that you should get more used to running pdk update
regularly as this is the standard way in which updated dependencies and support for new Ruby versions will be delivered.
Good luck, and happy Puppeting!