GeistHaus
log in · sign up

https://object.io/feed.xml

rss
10 posts
Polling state
Status active
Last polled May 19, 2026 05:34 UTC
Next poll May 20, 2026 02:32 UTC
Poll interval 86400s
ETag W/"68060cb1-16d65"
Last-Modified Mon, 21 Apr 2025 09:15:29 GMT

Posts

Setup pre-arm in betaflight
toolsdronebetaflightsafety

I have recently taken up FPV drone flying. It is both difficult and satisfying. /r/fpv has been a helpful community in getting started and finding great gear that works well together. One thing I came across was a recommendation to enable pre-arm on your drones, but it took some research to figure it all out as a beginner.

Setting up pre-arm means you need both hands engaged to arm your drone before flight. This can protect you from accidental arming a drone and getting cut on the propellers. Common stories include locating a downed drone and picking it up, the radio dangling on its leash and the arm button is bumped. Graphic pain follows.

Steps to setup pre-arm

The setup is easy and straight forward once you know how. It is directly supported in betaflight, so it will merely take a few minutes to configure and no advanced steps necessary.

Start out by considering what button on your radio you want to use for pre-arming. I picked a momentary switch (one that outomatically switches to OFF after clicking it) on the opposite side of the radio from the ARM toggle, just to make sure I need to use both hands to arm the drone.

  1. Using Chrome on your computer, open the betaflight web version at app.betaflight.com. This is easier than installing betaflight natively on your machine. As of the time of writing, only Chrome supports communicating with USB peripherals.
  2. Connect to your drone with USB or bluetooth.
  3. Click Modes menu on the left.
  4. Power on your Radio and connect it to the drone (your remote controller).
  5. Find “PREARM” in the list of modes in betaflight.
  6. Click “Add Range”, pick “AUTO” as the input in the dropdown.
  7. Toggle the switch on the radio you want to serve as your pre-arm switch. In this setup I use the momentary switch SF on the RadioMasterBoxer. The “AUTO” will now automatically switch to the actual button identity. Turns out this is AUX6 for this particular radio. Screenshot of betaflight mode for PREARM, without the button pressed See what the on/off outputs of your button are, and adjust the range to match by moving the sliders. I only want “pre-arm” to be true when the button is held. Screenshot of betaflight mode for PREARM, with the button pressed and the ranges adjusted

Once you think everything is setup right, click your chosen botton on/off a few times, and see if PREARM lights up when you expect it to. Click “Save” and you’re done!

Pre-arming before flying
  • Hold the SF momentary button.
  • Then arm the drone by switching the default SA switch ON.
  • Drone is now armed and ready to fly.
  • Disarming: switch SA to OFF and the drone immediately switches off.
Other options

The above is a setting within the specific drone. If you have multiple drones you want to behave the same, you could also configure a logical pre-arm sequence in your Radio.

https://object.io/site/2025/setup-prearm-in-betaflight/
Spelunking an ActionCable Error
rubyrailsactioncablepry

Upgraded an app to Rails 5.0.7, and after following the official migrations steps, the app completely failed to load.

The exception itself was rather puzzling: undefined method 'logger' for nil:NilClass (NoMethodError), raised deep inside of ActionCable at lib/action_cable/engine.rb:20:

initializer "action_cable.logger" do
  ActiveSupport.on_load(:action_cable) { self.logger ||= ::Rails.logger }
end

So, how can this seemingly simple code go wrong? Let’s read backward from what is calling the above on_load code-block.

According to the documentation for ActiveSupport::LazyLoadHooks, the code added to on_load(:action_cable) is triggered when run_load_hooks(:action_cable, context) is called at a later time, with a context object passed in. In this case, the context passed to on_load was unexpected a nil value.

Let’s look closer to what is actually passed to this run_load_hooks call:

ActiveSupport.run_load_hooks(:action_cable, Base.config)

Let’s check the ActionCable::Server::Base.config method definition:

cattr_accessor(:config, instance_accessor: true) {
  ActionCable::Server::Configuration.new
}

The intent here is the Base.config method lazy-initializes an ActionCable::Server::Configuration instance and re-uses that for subsequent calls.

Somehow that initialization fails to run in our case, and we get nil value returned instead.

Inspecting the weirdness

So, here’s how I finally figured out what was going wrong, with the above code reading context in mind: I used bundle open actioncable and edited the end of the lib/action_cable/server/base.rb file locally like so:

  require 'pry'; binding.pry  # <-- ADDED THIS LINE
  ActiveSupport.run_load_hooks(:action_cable, Base.config)
end

Source

I added gem 'pry-rails' to the project Gemfile, and started a Rails console.

Time to show where the Base.config method is actually defined:

[1] pry(ActionCable::Server)> show-method Base.config

From: ~/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/table_print-1.1.4/lib/table_print/cattr.rb @ line 9:
Owner: #<Class:ActionCable::Server::Base>
Visibility: public
Number of lines: 1

Surprise! Hello table_print-1.1.4, didn’t expect to see you here. That most likely explains why the lazy initialization was skipped, an old monkey-patch gumming up the works.

Removing table_print from the project Gemfile, and re-running the console, we get:

[1] pry(ActionCable::Server)> show-method Base.config

From: ~/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.0.7/lib/active_support/core_ext/module/attribute_accessors.rb @ line 60:
Owner: #<Class:ActionCable::Server::Base>
Visibility: public
Number of lines: 3

def self.#{sym}
  @@#{sym}
end

With the removal of the old gem, the Rails app succeeds in booting the Rails console. There was much rejoicing.

As we’re done debugging here, let’s clean up the edited actioncable gem:

$ gem pristine actioncable
Restoring gems to pristine condition...
Restored actioncable-5.0.7
A conclusion of sorts

Reading, understanding and messing with the Rails source are sometimes the only way forward when things behave unexpectedly. binding.pry is a good friend and show-method can really save the day, especially when the observed behavior doesn’t match the source-code.

Got any weird puzzling bugs holding your project back? I’d be happy to take a look. Contact details below.

https://object.io/site/2018/spelunking-actioncable-error/
Rubocop for Large Projects
rubyrailsworkflow

A typical first impression of running rubocop on a large project:

  669 files inspected, 1700 offenses detected

Where to start. I’ll tell you. Welcome to the “Rubocop for Large Projects” guide.

How did we get here?

Long-lived and valuable software projects typically pass though the hands of multiple maintainers. Each maintainer provide their unique insight and leave a fingerprint on the code. During the lifetime of a software project, the code is read many times compared to the times it is actually changed. There are many factors in how easy a piece of code is to understand, including your level of experience, whether you wrote it, and how well it is factored.

One simple aspect of code readability is at the very surface, the characters on the page. The consistency spacing, use or lack of newlines, compactness of complexity. Is the story of the system told consistently and compellingly, or is the essence of the system buried in irrelevant, potentially distracting, details? Does a simple change entail editing code in more locations than seems reasonable, and having to remember to do everything “just so”?

There are a countless ways of standardizing on code style. Personally I’ve found that the exact details of the style does not matter quite so much as the consistency of the code. Project contributions, including those for open source projects, are more readily accepted if you closely follow the style already found in the codebase, rather than trying to impose your personal style in your contribution “because cleaner”.

Getting started Tool Installation

Adding rubocop to a project is very simple. Usually you want it available in the “development” and “test” environments, so add it to your Gemfile like this:

group :development, :test do
  gem 'rubocop', require: false
end

Run the bundle command, then make a commit with these simple changes.

Configuring and Running

Invoking the command interactively is like you would expect, just a bundle exec rubocop. The output from the initial run is most likely overwhelming, or you would not be reading this. We will fix that right up.

Generate a configuration that accepts your code as it currently is, warts and all:

$ bundle exec rubocop --auto-gen-config
$ echo "inherit_from: .rubocop_todo.yml" > .rubocop.yml

Go ahead and commit the two files .rubocop.yml and .rubocop_todo.yml. If you now run the rubocop command, everything should be green and accepted. You now have a baseline to work from.

Overview of Operations Types of automated rules

There are four types of rules called cops in rubocop:

  • Lint cops check for things that frequently cause problems. A special always-enabled lint cop is Lint/Syntax which checks for syntax errors. Other lint cops include whether assignment in conditions are allowed and whether matching if and end should always line up. These are things that are a good idea to fix right away.

  • Style cops are based on the ruby style guide. These are rules covering whitespace in large detail, comment styles, formatting of hash and array literals, you name it, there’s probably a styler for it. Many of these cops can be configured to your preference. This means you can decide if you prefer hash-rockets => in your hashes, parenthesis around method arguments, and lots of other little details. The defaults match the ruby style guide, so if your codebase more or less follow that style, you will have an easy time. There are also quite a few variations on the code standard you can pick and choose from.

  • Metrics cops look at a bit higher level at code complexity measurements. Are classes and modules a reasonable length? Are methods sufficiently short for easy understanding? Are there an extraordinary number of parameters for a method, signifying underlying issues? Are there way way too many paths through a single method with a bunch of nested conditionals?

  • Rails cops are concerned with some best practices that are helpful in Rails projects, such as preferring Time.zone.now over Time.now to avoid issues with timezones, new vs. old style setup of validations, and a few other niceties. Enable these if your project is of the Rails persuation, they are not automatically enabled. Read on for details about how to do this.

Configuring rules and exceptions

Configuration is stored in a .rubocop.yml file in the root of your project. If your project matches the default ruby style guide settings, you can even skip having a configuration file altogether.

By default style, lint and metrics cops are enabled. Rails cops have to be enabled manually, simply for the reason that not all Ruby-code is Rails code. To enable Rails cops, simply create a new file .rubocop.yml and add this as the contents:

inherit_from: .rubocop_todo.yml

AllCops:
  RunRailsCops: true

For the very messy projects: You can even have a .rubocop.yml in a subfolder if something needs a whole lot of exceptions initially. Maybe your spec/ or test/ folder is going to require a whole lot of changes before the codebase is in a consistently readable state. That said, starting out you will probably appreciate keeping things simple and maintaining just a single .rubocop.yml file in the root of your project. This makes it abundantly clear what rules are enabled where.

Appropriate commit size for easy review

As we go through the rules and options you have, there will arise a great desire to Fix All The Things - Now. Using the power of modern version control, you can most certainly do that. The primary drawback I have observed is the enormous burden this places on the unfortunate human reviewers which have to read through one or a few commits that basically change the entire face of the code-base. You might even be that human, so for the sake of the reviewing human (especially if it is you), go slow and change things deliberately.

Craft each commit to focus on one or a few related issues, document what issues you have fixed, and update the rubocop configuration as you go to now forbid the issue you now fixed. This way you can send fewer changes at a time to be reviewed, meaning faster review, and less chance of a merge conflict from an unrelated concurrent code change. Feature additions have right of way when merging, and cleanup commits are most likely easier to re-create when handling conflicts. By keeping each commit small and focused (and as automated as you can), recreating a particular cleanup can be performed without guesswork.

Pass 1: Fixing all Lint warnings

First of all we are going to focus on fixing up all Lint warnings. These include problems flagged by ruby when you run with warnings enabled such as shadowed and unused variables.

Quick and automated wins

First of all we are going to fix the Lint issues that rubocop has an automated response to. Make sure you have a clean working copy - commit or stash your current changes. Create a new branch for this work, a good name would be chore/rubocop-round-1-fix-lint-warnings.

Then let us automatically fix some of the serious warnings in your codebase:

$ echo > .rubocop_todo.yml
$ bundle exec rubocop --lint --auto-correct

The first echo line will clear the current TODO file you might have, thereby allowing all automated linters to run in line two.

Typical issues this will fix are:

  • Unused variables for methods or blocks get a _ in front of their old name. That way it is clear they are intentionally not used. Read through these proposed changes carefully, you might find a bug or two this way.

  • Replacing File.exists? with File.exist? because the former is deprecated.

  • Smaller things like simplifying "Oh #{thing.to_s}" to "Oh #{thing}", where the .to_s call made no actual difference to the behavior.

Check through each of the changes, then commit them. Make sure you record the command you used here in your commit message, should you need to recreate this commit later on. Somebody else might have changed the integration branch while you were busy inspecting the changes, and it’s nice to be able to easily re-create the update without much effort. Since this particular commit is the result of an automated process, re-running it is a lot easier than starting to manually handle merge-conflicts. Just throw your branch away and re-create it from the latest integration branch if you need to - it should literally take seconds.

Clean up the rest

There will most likely still be some warnings left when running in --lint mode. To see the full name of warnings use the --display-cop-names flag like this:

$ bundle exec rubocop --lint --display-cop-names

There is an additional flag that can be helpful at this point called --display-style-guide. This adds a link to the ruby style guide for each infraction where it is relevant. Having more information about exactly what the underlying issue is can be quite helpful in determining what to do about it.

You can focus on a single of these issues, by running rubocop with a single linter at a time:

$ bundle exec rubocop --lint --only AssignmentInCondition
$ bundle exec rubocop --lint --only UselessAssignment

Work through each of the cases reported like this:

  • Update the source code manually as needed to fix the issue
  • Create a commit after fixing each issue. Make sure you add a note to the commit message with details of exactly what issue you have now fixed.
  • Re-run the full bundle exec rubocop --lint.
  • Pick one specific issue to fix
  • Keep this up until everything is in order.

Final step in preparing this changeset is to re-run this command:

$ bundle exec rubocop --auto-gen-config

Commit the updated .rubocop_todo.yml file. Expect this to a lot shorter than it was before you started fixing the Lint issues.

Have another team member review your changes, then enjoy your updated warning free codebase.

Automation

Finally, we are going to add a step to your Continuous Integration (CI) flow to ensure no new Lint issues arise moving forward. Simply add this single line as a step that has to pass for the build to be successful:

$ bundle exec rubocop --lint --display-cop-names \
  --display-style-guide --rails

Add it now, and make sure the build still passes.

With that addition to CI, we are done with the first pass fixing all the potential issues the Lint cops capture, and we are ready to take on making the code style consistent.

Pass 2: Easy style fixes

Agreeing on all details of a full code style takes an enormous effort. Much easier is to make gradual changes and gather feedback on what is prefered for each particular aspect.

  • Consistency for readability
  • Gradually make all whitespace consistent. Start small with Style/TrailingWhitespace and Style/TrailingBlankLines to get rid of any trailing whitespace and make newlines consistent across the project.
  • All the indentation you can eat
  • Strings and hashes (let’s all agree on something)
  • Placing the dots [Style/DotPosition - suggestion to support copy/paste: Style/DotPosition: EnforcedStyle: trailing]
  • Big number formatting [Style/NumericLiterals: ]
  • Method definitions - to paranthesise or not?
  • Strip out redundancies [Style/RedundantBegin, Style/RedundantReturn, Style/RedundantSelf]
  • Spaces and operators

Steps:

  • Pick a rule in .rubocop_todo.yml to remove that sounds promising.
  • Lookup the documention for the rule to figure out what configuration options are available. The most complete documentation is the rubocop defaults.yml. There you can see all the various options for each rule. Typically a rule is listed with examples of the effect of the various options.
  • Add the new configuration to your .rubocop.yml file
  • Remove the exception in the .rubocop_todo.yml file
  • Rerun rubocop --auto-correct to take care of the automated fixes.
  • Commit these easy changes.
  • Run rubocop to check for any instances that are not auto-fixable with the new rule in place.
  • Fix the issues manually and commit the resulting manual work.
  • A run of rubocop should now be green again - push your branch, create a Pull Request, and have somebody review the changes.

By keeping separate the automated and manual fixes it becomes easier to review, as the automated fixes are typically extremely similar and unremarkable. For the manual fixes a bit more care has to be spent in the review.

Pass 3: Start taming complexity
  • Large methods
  • Large classes or modules
  • The less obvious issues such as coupling and inconsistent or non-standard naming
Your new daily habits
  • Lower the limit on line length (usually raises class/module line count)
  • Break down a single complex class into more classes
  • Document why a single class exists
Final thoughts

This guide has hopefully helped you get started with Rubocop and applying it to your large project.

For some concrete examples of making a codebase a bit more consistent, you can read through my merge-requests to the errbit project

Related reading
  • Sandi Metz Practical Object-Oriented Design in Ruby, poodr.com, 2012
  • Nat Pryce and Steve Freeman Growing Object-Oriented Software, Guided by Tests, 2009
  • Martin, Robert C. Clean Code, 2009
https://object.io/site/2017/rubocop-for-large-projects/
Serving Rails assets with nginx
railsnginxassets

Using the Rails asset pipeline makes it safe to cache assets forever in browsers. Rails is configured for this out of the box, so you only need to ensure that you precompile assets before or during each deploy.

With that out of the way you can simply add this snippet to your nginx config to allow assets with digests in their name to be cached forever in clients:

# From https://object.io/site/2015/rails-nginx-easy-assets
#
# Cache forever publicly: files for generated assets
#   /assets/application-2565b50fc38a0b3a44882faa3e936262.css
#
# This setup means a CDN may cache these files
location ~ "^/assets/.+-[0-9a-f]{32}.*" {
  gzip_static on;
  expires     max;
  add_header  Cache-Control public;
}

# Try serving request as a static file, no caching:
location / {
  try_files $uri/index.html $uri.html $uri;
}

This gives special treatment to files in /public/assets/ with a 32-character hex digest in the filename. These files are exactly the ones built by the asset pipeline, and they are allowed to be publicly cached forever, as they are guaranteed to never change their contents. With this configuration in place you can also trivially use your nginx as the origin server for a CDN.

Any static files not matching the first rule are not allowed to be cached forever and will have to be re-requested with each visit. The more you can build as digested assets, the less strain you will see on your nginx server and your bandwidth bill.

Happy caching!

https://object.io/site/2015/rails-nginx-easy-assets/
Maintenance development - freestyle

How do you get started doing effective maintenance development? This topic is near and dear to me, as I’ve spent most of my career writing new functionality, but also to a large degree getting good at cleaning up and extending code written by others. There are some knacks to doing it well, and I’m in the process of writing write a practical guide to help you learn it faster.

Style guides are topic with the potential for endless discussion. The ruby community has some good ones, and you are of course free to write your very own as well. One thing you will typically find taking over a project that has lived for a while, maybe with multiple maintainers, is inconsistent code styles. This is obviously not the end of the world, but it can sidetrack you when trying to reason about the current behavior of a piece of non-obvious code.

Fortunately it is easy these days to setup rubocop and ensure a consistent style is used. It is even partially possible to automatically clean up most of it to conform. Consistent hash style, yay.

Some large codebases are a beautiful and valuable mess. They often have a large gap between the current state and an ideal state of syntax consistency and well-managed complexity. All is fortunately not lost, The Rewrite is typically not the answer. Instead, a processes is needed to break things down and attacking the problem in smaller chunks over a period of time, without halting all other progress. It is such a process I’m describing in the guide.

Update

The guide is ready and now available as a free article on getting started with Rubocop for Large Projects. Enjoy!

https://object.io/site/2015/maintenance-programming-freestyle/
Upgrading ruby hash syntax
rails

Consistency makes code easier to read. Long-lived projects can be a wildly inconsistent in their ruby hash syntax, so here is a quick way of upgrading all symbol-based hash-keys to the new leaner format. Simply put: perform the below operation to convert :thing => 'value' to thing: 'value' in one easy step.

Please remember to commit everything first so you have a clean slate to start out from, and undo is easy.

In Sublime Text, open Find | Find in Files... and enter:

Search:   ([^:]):([^:\s=]+)\b\s*=>\s*
Replace:  \1\2:

Ensure the Regular Expression mode is active (the button looks like: .*). Now is a good time to use the File | Save All action.

Remember to double-check everything looks just right. I recommend using git add --patch . for this, because that way you can easily skip any changes you might not agree with.

This is just a minor detail in the overall readability and consistency of a project, but even minor details matter when it comes to long-term ease of maintainance.

Update 2015: Rubocop can easily fix this minor issue and also automatically fix quite a few other inconsistencies. Highly recommended.

https://object.io/site/2014/upgrading-ruby-hash-syntax/
Flash of Convenience
railsi18nworkflow

Flash messages in Rails applications need a bit of love. A lot of them tend to be identical within a given app, and it’s a bit annoying to manage defaults and fallback for all these calls to I18n.t(). The following is a small helper-method and a simple structure to keep it all neatly organized.

For this to work, add the text you want shown to your config/locale/en.yml (by default):

en:
  posts:
    create:
      flash:
        created: "Your post will now be reviewed before publishing"

  flash:
    created: "Successfully created"
    updated: "Successfully updated"

Here the posts#create action has a custom flash for the :created flash-message. All other controllers and actions in the application where a flash-message of :created is used the default message of "Successfully created" will be shown.

Next, add this to your ApplicationController (this is all of the magic):

  protected
  def flash_message cause, args = {}
    primary_key = "#{controller_name}.#{action_name}.flash.#{cause}"
    default_key = "flash.#{cause}".to_sym
    I18n.t primary_key, args.merge(default: default_key)
  end

Finally, this is how you use it within an action:

  flash[:notice] = flash_message(:created)
  # and
  redirect_to :root, notice: flash_message(:created)

Try to keep the actions somewhat generic like :created, :deleted, :updated, etc. This makes the default values easier to manage.

What just happened?

The I18n system has some neat convenience shortcuts we’re using here. First of all we build the primary flash lookup-key based on the current controller and action.

Second bit of functionality used here is the fallback message. Fallback messages can be given as an explicit string, or a symbol. When a symbol is used it will be used to lookup a new message in the I18n backend. We use that for the :default argument where the fallback is set to be a global flash message like flash.created.

Now you can customize all the flashes in your app without touching the code, you have all these small bits of user interface in a single file for easy overview. You can also easily vary the messages between languages, customize text where it is necessary, and have a convenient global fallback message.

https://object.io/site/2012/flash-of-convenience/
Getting to know Ruby debugger
rubydebugworkflow

This is a guest-post by Cameron Dykes, about getting started with the ruby debugger.

A key step to debugging any program is replicating the environment to ensure you can consistently produce the bug. In my early Ruby days, to inspect the environment, I used a primitive method: placing puts lines in my code to print values to the console (let’s call them “inspection puts”).

It may have looked something like this:

class Buggy
  # assuming perform_operation and special_options exist...
  def buggy_method(param=nil, options={})
    puts "\n\n\nDEBUG"
    puts "param: #{param}"
    puts "options: #{options}"
    @instance_var = perform_operation(special_options(param, options))
  end
end
The case for ruby-debug

This method very easily gives me the information I need, but it has some downsides:

  1. To inspect the return value of special_options I have to add another inspection puts.
  2. Every addition of a new puts requires that I restart the application to inspect the results.
  3. To inspect how special_options and perform_operation are handling the data, I have to add inspection puts inside of them.
  4. I must remember to remove all of the inspection puts before I push the code.

If only there was a better way to do this.

ruby-debug to the rescue! By putting a breakpoint in our code, we have the ability to inspect environment state, check the return value of any methods, and step through our code one line at a time. This is much more versatile than inspection puts because the full environment is available to us. The “I wonder what the value of this is” problem is gone since we can inspect whatever we want. The step functionality the debugger gives us is useful as well, allowing us to step inside of a called method while maintaining the interactive environment.

Setting up ruby-debug

To get set up with the debugger, we’ll need to install the gem:

# Using MRI-1.9.2
gem install ruby-debug19

# Using MRI-1.8.7 or Ruby Enterprise Edition
gem install ruby-debug
ruby-debug in action

Let’s update the example from above using a debugger breakpoint instead of inspection puts:

class Buggy
  # assuming perform_operation and special_options exist...
  def buggy_method(param=nil, options={})
    require 'ruby-debug'; debugger
    @instance_var = perform_operation(special_options(param, options))
  end
end

The next time the method is called, the debugger will stop and give us an interactive shell. We can inspect the values of each variable with the eval command:

eval param
eval options

We can also see what the return value is of the invoked methods:

eval special_options(param, options)
eval perform_operation(special_options(param, options))

We can even use the step command to enter inside of special_options and perform_operation to see what they do.

Here are the various debugger commands that I most commonly use:

  • list, l - show the code for the current breakpoint
  • eval, e - evaluate expression and print the value
  • step, s - next line of code, moving within methods
  • continue, c - continue in the program until the program ends or reaches another breakpoint
  • quit, q - abort the program

Many more commands are available, which can be seen by entering help in the debugger.

Better debugging ftw!

With the ruby-debug gem, we have a better tool for diving in to our code than inspection puts. Using a debugger breakpoint, we can interactively step through our code with less cleanup.

Happy debugging!

Debugging references Who am I?
https://object.io/site/2011/getting-to-know-ruby-debugger/
Deploy a private Github repository with whiskey_disk
deployrubygitworkflow

whiskey_disk makes it very easy to quickly deploy a new version of your site to one or more servers. It’s very efficient, as files are copied directly from your git repository to the server. This means you can very quickly make large deployments even when you are on a slow internet connection. You do need a secure way for your server to access the git repository while the deploy is going on, read on to see how this is easily done.

For this example, I’m going to deploy a small static site from a private Github repository to a server via ssh using whiskey_disk. There are a few steps in getting this all setup, but once you have the ssh infrastructure playing nicely, a complete deployment of a small static site takes on the order of 2-5 seconds. Stay with me, and I’ll show how all the pieces fit together.

To begin with, I’ll assume you have a site or application already pushed to a private Github reposity that you now want to do a deploy of. You also have ssh access to the server you are deploying to for this to work. For this example, I’m deploying the master branch of the following private git repository:

git@github.com:rud/whiskey_disk-demo.git

Adding the configuration file

Make sure you have your appropriate code pushed to Github, then we’ll add the whiskey_disk config file config/deploy.yml to the project:

demo:
  domain:     "deploy_user@example.com"
  deploy_to:  "/home/deploy_user/domains/useful_site.com"
  repository: "git@github.com:rud/whiskey_disk-demo.git"

This is a simple YAML file (indentation matters), you will need to adjust the domain and paths as appropriate. I use the name demo for the environment, other common examples would be staging, production, etc. A lot more options are available, see the official whiskey_disk readme, but this all we need to get started.

Running whiskey_disk setup

whiskey_disk has two commands: a setup command, for doing the initial repository clone, and the deploy command used for all subsequent deploys.

With the config/deploy.yml file in place inside the project, we’re going to just try and run the setup command from the local computer, seeing what happens. For now, this errors out:

$ wd setup -t demo
Initialized empty Git repository in /home/deploy_user/domains/useful_site.com/.git/
Host key verification failed.
fatal: The remote end hung up unexpectedly
fetch-pack from 'git@github.com:rud/whiskey_disk-demo.git' failed.
error: pathspec 'origin/master' did not match any file(s) known to git.
Did you forget to 'git add'?

Okay, we’re not quite ready to do deployments just yet.

Resolving all ssh issues

Github hangs up on us - what’s happening? Testing from my local machine as described in the github ssh guide:

$ ssh -T git@github.com
Hi rud! You've successfully authenticated, but GitHub does not provide shell access.

That’s the kind of success we’re looking for on the server as well. Ssh to the server as the deploy user, and try the same command there:

$ ssh -T git@github.com
The authenticity of host 'github.com (207.97.227.239)' can't be established.
RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'github.com,207.97.227.239' (RSA) to the list of known hosts.
Permission denied (publickey).

Try the command again, now that the host key is trusted:

$ ssh -T git@github.com
Permission denied (publickey).

Notice how we get a new error later in the connection process, a Permission denied (publickey) instead of Host key verification failed.

For the sake of completeness, this is what this situation looks like from whiskey_disk:

$ wd setup -t demo
Initialized empty Git repository in /home/deploy_user/domains/useful_site.com/.git/
Permission denied (publickey).
fatal: The remote end hung up unexpectedly
fetch-pack from 'git@github.com:rud/whiskey_disk-demo.git' failed.
error: pathspec 'origin/master' did not match any file(s) known to git.
ssh-agent to the rescue

The root cause for this difference is Github knows my public key and has access to the corresponding private key, but this key is not available to my server, so it cannot authenticate against Github. The solution is fortunately simple. Do this on your local machine:

$ ssh-add

This prompts you for the password for your private key, then keeps the private key unlocked in memory for automated access. You need to do this after each time you restart before you can do new deployments, as the unlocked key is only stored in memory. We also need to enable ssh-agent support for the connection to the server - as ssh-agent gives a server the possibility to use our private key without further prompting, you need to trust the administrator of servers you deploy to. With that caveat out of the way, open your ~/.ssh/config file, and add a config block like this:

Host example.com
  ForwardAgent yes  # This enables ssh-agent functionality, making whiskey_disk happy

Adjust the hostname to taste, obviously. You can also add port-number here, if your admin moved ssh access from the default port. While you have that file open, also check out these extremely handy ssh productivity tips for a lot of extra ssh goodness.

Close the previous connection to your server, open a new one (so ssh-agent can do its magic), then re-run the test:

$ ssh -T git@github.com
Hi rud! You've successfully authenticated, but GitHub does not provide shell access.

We have ssh-win!

Deploying

Time to re-try the deployment setup from the local machine:

$ wd setup -t demo
Results:
deploy_user@example.com => succeeded.
Total: 1 deployment, 1 success, 0 failures.

This means the repository was successfully cloned into place. We only have to do this once, from now on deployments are just a matter of:

$ wd deploy -t demo
Results:
deploy_user@example.com => succeeded.
Total: 1 deployment, 1 success, 0 failures.

I’m seeing a total time taken for a deployment in the area of 2-5 seconds. Remember to push to Github before running your deployment, as it is from Github your server is pulling versions.

This method for deployment is extremely awesome, and the amount of awesome grows with the size of your deployments, as server-server connections are most likely a lot faster than your own internet connection.

Next steps

There is a lot more to whiskey_disk that I’ve shown here. In particular I’d like to highlight the “Configuration Repository” section of the official whiskey_disk readme. Editing per-application configuration files on individual servers is effectively a thing of the past.

https://object.io/site/2011/whiskey_disk-private-github/
Modernize Your Git Workflow
toolsgit

Years ago I read Streamed Lines: Branching Patterns for Parallel Software Development, and many of the thoughts have stayed me. Think of it as a catalogue of Design Patterns for version control workflows. There are so many ways to efficiently use branches to both collaborate and isolate work. Lively discussions can be had on how to best organize a repository, and having a great overview of the options makes for more informed discussions.

I have previously described how I’ve been using small hack && ship commands to simplify and streamline my personal git workflow. Recently I’ve switched to using the Git Flow setup as my branching and release workflow.

Setting up git-flow

Installing the git-flow toolset on OS X is trivial using the homebrew installer:

$ brew install git-flow

The git-flow project also has instructions for installation on Linux and Windows.

Installing git-flow adds a few helpful commands to the git environment for creating and managing branches for features and releases. A fresh git repository is born with a master branch. In the default git-flow setup, this is where the current production release lives. Furthermore, a branch called develop is created, this is where development takes place. Note that git-flow is just a series of shortcuts to having a development branch and a production branch with sensible ways of shuttling changes back and forth. After installing the git-flow package, configure your local repository for git-flow use with:

$ git flow init

You can most likely accept the defaults by pressing enter at each question - this also makes it easier for others to initialize git-flow in their working copy, as all are using the same defaults. The only change from running the init command is two [gitflow] sections in your .git/config file looking like this:

[gitflow "branch"]
  master = master
  develop = develop
[gitflow "prefix"]
  feature = feature/
  release = release/
  hotfix = hotfix/
  support = support/
  versiontag =

It’s no more magic than that. Consult the built-in help with:

$ git flow

If you’ve got the bash-completion package installed, you can use tab-completion for git-flow commands and arguments. Highly recommended.

Working on a feature

Start work on a feature - say feature 77 from your issue-tracker:

$ git flow feature start 77-speedup-yak-shaving

This creates a new regular local git branch called feature/77-speedup-yak-shaving based on the current develop and places you on it. Want to share your work in progress with others? Use:

$ git flow feature publish 77-speedup-yak-shaving

This pushes the branch, and sets up your local branch to track the remote branch in one easy step. The regular git push and git pull --rebase work as you would expect here. There is nothing special about branches created by git-flow, they are just conveniently named and managed.

All done with a feature? Rebase your feature on the current develop branch, then merge it in:

$ git flow feature rebase
$ git flow feature finish 77-speedup-yak-shaving

With these two steps, you end up on develop with your feature merged in. Run your test-suite, and push to a central repository as appropriate.

Releasing to production version and hotfixes

Production releases are handled quite nicely in git-flow:

$ git flow release start 2011_year_of_the_yak

This creates a new branch called release/2011_year_of_the_yak, based on the current develop branch. Here you can fix any HISTORY or VERSION file, commit, and the finish up the release with:

$ git flow release finish 2011_year_of_the_yak

By finishing the release, you now get a tag called 2011_year_of_the_yak, and the temporary release-branch is removed. You end up back at the master branch.

A hotfix is simply a feature branch which is based on the latest production release, and which is automatically merged to both develop and master. Extremely handy.

Rebase considered harmful?

Some developers prefer to only ever merge, but I’m a big fan of rebasing local work before publishing. I’ll leave you with this:

If you treat rebasing as a way to clean up and work with commits before you push them, and if you only rebase commits that have never been available publicly, then you’ll be fine. If you rebase commits that have already been pushed publicly, and people may have based work on those commits, then you may be in for some frustrating trouble.

From Pro Git, chapter 3

https://object.io/site/2011/enter-git-flow/