Using VS Code as a Rails app:update merge tool
Background: the rails app:update
command
As part of bumping the major or minor version of a Rails app, one should execute the rails app:update
command (docs). This command is intended to help maintainers of existing Rails applications to update their application configuration files as appropriate to accommodate changes that have been made to Rails’s configuration files and changes of the configuration defaults in the newer version of Rails.
This command will compare, one by one in an interactive console session, the content of the configuration files (such as config/application.rb
, config/environments/production.rb
, etc.) that would be generated for a brand new Rails app against the current actual content of those same files in the existing application that is being upgraded.
The developer performing this upgrade will want, in many cases, to keep their existing application configuration, including some departures from the Rails default configuration that was provided by the initial rails new
command that generated the app. In other words, most application configurations will contain some adjustments versus the out-of-the-box Rails defaults, and many of the reasons for those adjustments will still be relevant and appropriate, even after upgrading to the newer version of Rails.
However, in other cases, an application might want to make some additions, changes, or even deletions with respect to its configuration, in response to newly available configuration settings that have been added in the new version of Rails, a change that has been made to the default of a certain configuration, or some other aspect of the framework.
Using a merge tool will make this task easier
Comparing and understanding the differences between the new default configuration files that Rails would generate for a brand new application versus the current state of the configuration files for an existing application (generated by an earlier version of Rails) is not an easy task, nor is resolving those differences to create a final version of the configuration files that will be used by the app going forward, synthesizing both the old and the new. Using a merge tool as part of this process, though, can be a big help.
Running rails app:update
When you execute the rails app:update
command, then for each configuration file where there is a difference between what Rails would generate for a new application versus the app’s existing configuration, the tool will note that there is a conflict, and it will wait for input about what to do:
❯ bin/rails app:update
conflict config/boot.rb
Overwrite /home/david/code/david_runger/config/boot.rb? (enter "h" for help) [Ynaqdhm]
Entering h
for help tells us what the options ([Ynaqdhm]
) stand for:
Y - yes, overwrite
n - no, do not overwrite
a - all, overwrite this and all others
q - quit, abort
h - help, show this help
d - diff, show the differences between the old and the new
m - merge, run merge tool
Either totally overwriting the existing configuration file with the default contents for a brand new Rails app (Y
) or ignoring the new configuration changes and just keeping the existing config (n
) would be easy options, but doing that risks losing some existing configuration that we want to preserve or missing out on relevant new changes in the Rails framework.
Although it’s more work than those simple options, in my opinion, the best option by far is the last one: m - merge, run merge tool
. However, if we choose this, we’ll get an error message (and then will be re-prompted to choose again):
Overwrite /home/david/code/david_runger/config/boot.rb? (enter "h" for help) [Ynaqdhm] m
Please specify merge tool to `THOR_MERGE` env.
Overwrite /home/david/code/david_runger/config/boot.rb? (enter "h" for help) [Ynaqdhm]
Instead, let’s exit the app:update
interactive session by hitting Ctrl-C, and, as the error message suggests, we’ll provide a THOR_MERGE
environment variable, which will tell the app:update
command which program to use as a merge tool to reconcile the diff between our app’s current configuration file and the version of the configuration file that would be generated for a fresh app by the new version of Rails to which we are upgrading.
Since I already use VS Code as my primary code editor (including using its merge editor to resolve git conflicts), I already have it installed and running, and I’m familiar with how it works. So, I like to use VS Code as the merge tool for rails app:update
. However, it’s not immediately obvious how to do this. The next section explains how.
How to use VS Code as an app:update
merge tool
Background: code --merge
The code --help
output includes this:
❯
code --help
[…]
-m --merge <path1> <path2> <base> <result>
Perform a three-way merge by providing paths for two modified versions of a file, the common origin of both modified versions and the output file to save merge results.
Background: rails app:update
and THOR_MERGE
For each configuration file that needs to be updated, the rails app:update
command will provide two file paths to the THOR_MERGE
tool:
- The first argument will be the path of a temporary file that will contain the version of the configuration file as Rails would generate that file for a brand new application.
- The second argument will be the path of that configuration file in our existing app.
Putting it together
So, the THOR_MERGE
program that we use will receive from the app:update
tool only two files, but, as seen in the code --help
output above, VS Code’s --merge
functionality needs to be provided with four files, which it calls <path1>
, <path2>
, <base>
, and <result>
.
The trick is that we will create copies of one of the files provided by app:update
, and we’ll use these copies as the additional file arguments required by code --merge
. To accomplish this, we can write a bash script, which we’ll provide as the THOR_MERGE
tool. You can put this bash script anywhere. You might or might not want to commit the script to your repository. I’ll create the file at bin/app_update_thor_merge_tool
, and we also need to make it executable:
touch bin/app_update_thor_merge_tool
chmod +x bin/app_update_thor_merge_tool
Then, paste this into the bin/app_update_thor_merge_tool
file:
#!/usr/bin/env bash
RAILS_DEFAULT_CONFIG=$1
APP_CURRENT_CONFIG=$2
mkdir -p /tmp/rails-app-update
PATH2=$(mktemp "/tmp/rails-app-update/current_XXXX.rb")
BASE=$(mktemp "/tmp/rails-app-update/base_XXXX.rb")
cp "$APP_CURRENT_CONFIG" "$PATH2"
cp "$APP_CURRENT_CONFIG" "$BASE"
code --merge "$RAILS_DEFAULT_CONFIG" "$PATH2" "$BASE" "$APP_CURRENT_CONFIG" --wait
rm "$RAILS_DEFAULT_CONFIG"
Then, run rails app:update
again, this time providing our script as the THOR_MERGE
tool.
Note: It’s important to provide the THOR_MERGE tool as an absolute path. Since your absolute path will be different from mine, you’ll need to tweak the command below to reflect the absolute path to the location of your app_update_thor_merge_tool
file.
THOR_MERGE=/home/david/code/david_runger/bin/app_update_thor_merge_tool bin/rails app:update
Now, if we press m
(for merge, run merge tool
) when prompted with a config file that needs attention, VS Code will open its three-way merge view:
The left pane shows the content of the config file as Rails would generate it for a brand new application.
The right pane shows the content of the config file as it currently exists in our repository.
The center pane is what will become the final result, which it is your job to craft, using the contents of the left and right panes of the merge view as a starting point and guide.
Read through any relevant Rails documentation about the changes and do whatever else you need to do to figure out how to resolve the discrepancies between your existing configuration and the configuration that Rails would generate for a new app. Then, once you have the middle pane into the desired final state, save the file, and close the tab in VS Code. In your terminal, the rails app:update
command will then move on to the next file, which you can again hit m
to open in the VS Code three-way merge view. Keep doing this, until you work through all of the configuration files that need attention.
Congratulations! You have now completed an important step in upgrading your Rails application.