How to apply environment specific changes within the classic Git branching model

branchingconfiguration-managementdeployment

I understand classic branching and deployment model as saying (in some small variant of the below):

  1. makes the changes on dev branch
  2. merge dev > staging branch, deploy to staging server, test it out
  3. merge dev > production branch, deploy production server.
  4. dont make commits directly on staging or production branches. Only merge changes into those branches from dev.

My issue is with point (4): I have some files that will be different depending on whether I'm running on dev, staging or production. These may include things such as robot.txt, server launch file (maybe staging is on Heroku and launch with a Procfile while production is on EC2), etc.. things that are deployment version specific and not application version specific.

The branching model above seems to say that right after a full deployment run, production = staging = dev. However, it does not leave space for those adjustment between the different environments.

How is this solved in industry?

===== solutions I can think of =======

  1. using environment variables on each env. Does the job for in code config switches. I already use this but I don't see how this can effect changes that are outside application code.

  2. adding deployment changes as commits directly on staging branch and production, and merging in application changes from dev (e.g. staging maintains a different version of robot.txt then production). Seems to contradict point (4)


Best Answer

  • Git is not suitable for managing different variants of your code.
  • Git is not a deployment mechanism.
  • Git is just a source code management system.

One thing that gets lost when using git push as a glorified FTP command is that source code is source code, and not necessarily the same thing you deploy. That even holds for interpreted systems where the source code can be executed directly.

One way to solve this is to introduce a build and deployment process that gathers the necessary files and deploys them to a certain target. Your source tree in Git might then be in a valid state for running it in a development mode, but for your production targets different config files will be used. For example, but not necessarily, a Docker image could be such a deployment format.

If a deployment target (such as Heroku) expects you to deploy via Git, it would be possible to use another repository or branch separate from your source code that contains the state to be deployed. This feels rather hackish, but actually works very well. However, this branch/repo does not contain any source code that needs to be version controlled since it's mostly a duplicate of the actual source code (from which it can be rebuilt at any time). It may therefore be occasionally desirable to prune these branches. You also lose a direct association between the deployed commit and the corresponding source code commit. Heroku may also offer other deployment types that do not abuse Git, but I'm not aware of those.

If a deployment target lets you run run pre-deployment scripts, that would be another solution. Such a script can then set up the necessary environment. For that to work, this script must be ignored by other targets or can detect under which target it is running.

In any case, adding deployment target-specific commits during your release process that are not expected to be merged back into dev is an antipattern. These will either get removed in a merge, or never become part of the history of other branches. Git is not good at maintaining a set of changes that only live on one branch if you want to merge other branches.

Related Topic