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.
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.
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.
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'
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.
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.
bundle gem
Rakefile
and *.gemspec
to prevent pushes to public rubygems.orgversion.rb
, remove changelog info, commit and rake release
Voilá!