Search code examples
capistranocapistrano3

How to replace a shared file when deploying code with Capistrano?


Update: TL;DR there seems to be no built-in way to achieve this, so a custom task is an easy solution.

Capistrano provides facilities to share files and directories over all releases. This is convenient and provides even some safety on files that should not be easily changed (or must remain the same across releases), e.g. a database configuration file.

But when it comes to replace or just update one of these shared files, I end up doing it manually, directly on the target machine. I would like to improve on that, for instance by asking Capistrano to overwrite some or all shared files when deploying. A kind of --force flag with some granularity.

I am not aware of any such kind of facility, and failing so far in my search. Any pointer?


Thinking about it

One of the reason why this facility does not exist (except that I did not find it!) is that it may be harder than it looks. For example, let's assume we have a shared database configuration file, and we exclude it from version control for security reason (common practice). Current release relies on version 1 of the DB configuration. The next release requires version 2 of the DB configuration. If the deployment goes well, everything's good. It gets harder when rolling back after some error with the new release (e.g. a regression), as version 1 must then be available.


Such automation would be cool and convenient, but dangerous as well. Yet I have practical use cases at hand.


Solution

  • I created a template method to do this. For example, I could have a task like this:

    task :create_database_yml do
      on roles(:app, :db) do
        within(shared_path) do
          template "local/path/to/database.yml.erb",
                   "config/database.yml",
                   :mode => "600"
        end
      end
    end
    

    And then I have a database.yml.erb template that uses things like fetch(:database_password) to fill in appropriate values. You can use the ask method in Capistrano to prompt for these values so they are never committed.

    The implementation of template can be very simple: you just need to read the file, pass it through ERB, and then use Capistrano's upload! to place the results on the server.

    My version is a little more complicated than yours probably needs to be, but in case you are curious:

    https://github.com/mattbrictson/capistrano-mb/blob/7600440ecd3331945d03e059368b75849857f1fb/lib/capistrano/mb/dsl.rb#L104