Authoring a Private Gem with Gemfury

Posted on July 03, 2017 by Carsten Zimmermann

We make an effort of extracting shared code into Gems. While FOSS code goes to RubyGems.org of course, our business-internal logic needs a private Gem server. We use Gemfury as our host but you can also roll your own with bundler/gemstash or Gem in a Box. This article focuses on my workflow with Gemfury.

Note: we are not affiliated with nor are we sponsored by said provider.

Gem Skeleton

In order to create all the boilerplate code and to make sure I am following the latest conventions, I use Bundler’s gem command:

bundle gem lolwat --no-coc --no-mit

When I cd into the created directory and load the generated lolwat.gemspec into my editor, I can see that it already includes a setting for allowed_push_host. This prevents me from accidentally pushing proprietary code to RubyGems.org.

Unfortunately, Gemfury.com doesn’t support a RubyGems compliant API to actually push my released Gem so there’s no need to bother with changing the example host to something real.

Instead, we’ll add the following line to the project’s Rakefile:

ENV['gem_push'] = 'no'

We’ll get back to it later.

Gemfury Support

In order to easily push to Gemfury.com, there a few more additions. First we have to include the gemfury Gem to our lolwat.gemspec file:

spec.add_development_dependency "gemfury"

The second file we need to edit is our Rakefile again. Add the following code to the end of the file:

# Rakefile
desc "Push lolwat-#{Lolwat::VERSION}.gem to Gemfury.com"
task :gemfury do
  package = "pkg/lolwat-#{Lolwat::VERSION}.gem"
  if File.exist? package
    system "fury push #{package}"
  else
    STDERR.puts "E: gem `#{package}' not found."
    exit 1
  end
end

Here’s a set of shell commands that will automate all this:

GEMNAME=$(basename $(pwd))
tail -r <$GEMNAME.gemspec \
  |sed -E '1s/(.+)/\1\'$'\n  spec.add_development_dependency "gemfury"/' \
  |tail -r >_new.gemspec \
  && mv _new.gemspec $GEMNAME.gemspec

GEMVERSION="$(grep module lib/$GEMNAME/version.rb|sed 's/module //')::VERSION"
cat >>Rakefile <<EOF

desc "$GEMNAME-#{$GEMVERSION}.gem to Gemfury.com"
task :gemfury do
  package = "pkg/$GEMNAME-#{$GEMVERSION}.gem"
  if File.exist? package
    system "fury push #{package}"
  else
    STDERR.puts "E: gem \`#{package}' not found."
    exit 1
  end
end

desc "Gemfury"
task :release do
  Rake::Task[:gemfury].invoke
end
EOF

Note: This has only been tested on Mac OS which uses BSD sed. GNU sed (Linux) may behave differently.

Prepare Release

While I’m hacking, I track completed features in a Changelog section in my README.md. I list the changes under a HEAD subsection.

## Changelog
### HEAD (not yet released)
* Add foo to bar
* Fix: Baz not correctly responds to boo

Be sure to add your git origin as explained by GitHub, GitLab, Bitbucket or whichever provider you chose.

For the first version, lib/lolwat/version.rb is already preconfigured with 0.1.0. That’s a sensible default for a first version to play around with. For later updates, increase the version number based on Semantic Versioning (»Semver«).

I prefer tracking the Changelog using the GitHub’s Release system (I’ll get to that in a bit). To that end, I remove the bullet points in my temporary »HEAD (not yet released) and commit the changed README.md together with the changed lib/lolwat/version.rb:

git commit -m 'Release v0.2.0'

Release the Kraken Gem!

Together with our changes to the gemspec and Rakefile, releasing the version is as simple as:

rake release

It will create a git version tag, push everything to your git remote, package the gemfile and upload the Gem file to Gemfury.

The last step is to head over to your GitHub project page, select releases and Draft a new Release in the upper right corner.

GitHub Releases Overview

The ensuing form is pretty self-explanatory (check the explanations on the right side). For the release name, I just repeat the tag name (e.g. v0.2.0). The text area gets all the info from my temporary Changelog tracker in the README.md file.

Summary

  1. Create gem skeleton using bundle gem
  2. Adapt Rakefile and *.gemspec to prevent pushes to public rubygems.org
  3. Hackedy-hack-hack + track features/bugfixes in README
  4. Update version.rb, remove changelog info, commit and rake release
  5. Create release on GitHub and paste changelog data

Voilá!

comments powered by Disqus