6.0 KiB
There are cases in which two or more dotfiles are very similar. For example,
two files exporting environment variables for two projects built with the same
technology (eg. two node.js web servers deployed on AWS). In these cases it's
nice to share as much code as possible across the dotfiles, by leveraging
templating and merging them in the same dotfile in Dotdrop's dotpath. Here
are a few suggestions about how to achieve this.
Brute force templating
The first approach is sheer use of templating and variables In order to do this, we need to:
- Create the merged dotfile with an arbitrary name somewhere in
dotpath. - Create two
dotfileentries inconfig.yaml, both having the merged dotfile assrc, but their owndst.
Here we have an example:
The merged dotfile in dotpath (dotpath/projects/env):
# .env
{%@@ if _dotfile_key == 'server0-env' @@%}
{%@@set aws_db_host = 'super-duper.host' @@%}
{%@@set aws_db_port = 4521 @@%}
{%@@ elif _dotfile_key == 'server1-env' @@%}
{%@@set aws_db_host = 'cheaper.host' @@%}
{%@@set aws_db_host = 9632 @@%}
{%@@ endif @@%}
export DB_HOST='{{@@ aws_db_host @@}}'
export DB_PORT='{{@@ aws_db_port @@}}'
Part of Dotdrop config.yaml file:
# config.yaml
dotfiles:
server0-env:
src: projects/env
dst: ~/projects/server0/.env
server1-env:
src: projects/env
dst: ~/projects/server1/.env
Installing the dotfile server0-env will create an environment file in
~/projects/server0/.env with the following content:
# .env
export DB_HOST='super-duper.host'
export DB_PORT='4521'
Profile variables
Albeit flexible, the previous method is a bit cumbersome for some use cases. For example, when the dotfiles belong to different profiles, the cleanest solution consists in using profile variables. This is achieved by:
- Creating the merged dotfile with an arbitrary name somewhere in
dotpath. - Adding some variables in the merged dotfile via templating.
- Overriding them with different values in each profile via profile variables.
- Typically, making the dotfile
dstdynamic, as different profiles need usually to deploy the dotfiles in different locations.
NOTE: This technique does not require two different dotfiles entry in
config.yaml.
An example:
The merged dotfile (dotpath/projects/env)
# .env
export DB_HOST='{{@@ aws_db_host @@}}'
export DB_PORT='{{@@ aws_db_port @@}}'
Part of Dotdrop config.yaml file:
# config.yaml
dotfiles:
env:
src: projects/env
dst: '{{@@ server_path @@}}/.env'
profiles:
server0:
dotfiles:
- env
variables:
aws_db_host: super-duper.host
aws_db_port: 4521
server_path: ~/projects/server0
server1:
dotfiles:
- env
variables:
aws_db_host: cheaper.host
aws_db_port: 9632
server_path: ~/projects/server1
With this setup, installing the server1 profile will create an environment
file in ~/projects/server1/.env with the following content:
# .env
export DB_HOST='cheaper.host'
export DB_PORT='9632'
Jinja macros
Even though it has cleaner dotfiles, the profile-variable-based procedure can't be used in two scenarios: when the dotfiles belong to the same profile, and when variable values require some complex computations. In both cases the brute force templating approach can be used, but in the latter one it also makes the dotfiles bloated with "bookkeeping" logic, thus hard to read.
A solution for this relies in leveraging Jinja macros. This method is a
variation of the brute force templating one, where the merged dotfile is
included from many different dotfiles in dotpath via jinja macros, rather
than via many dotfile entries with the same src attribute. This way, the
merged original dotfiles stays clean as in the profile variables solution,
because computations are in other files.
The steps to achieve this are:
- Creating the merged dotfile with an arbitrary name somewhere in
dotpath. - Wrapping the whole content of the merged dotfile in a jinja macro, with the necessary parameters.
- Calling the macro in each original dotfiles, computing the parameters there.
NOTE: The merged dotfile will be empty, as it only contains a jinja macro.
If it needs not to be deployed, the ignoreempty entry can be set to
true in config.yaml.
As usual, an example:
The merged dotfile in dotpath (dotpath/projects/env):
{%@@ macro env(db_host, db_port) @@%}
# .env
export DB_HOST='{{@@ db_host @@}}'
export DB_PORT='{{@@ db_port @@}}'
{%@@ endmacro @@%}
Server0's environment file (projects/server0/.env)
{%@@ from projects/env import env @@%}
{%@@ set keyPieces = _dotfile_key.split('-') @@%}
{%@@ if keyPieces[-1] == 'dbg' @@%}
{%@@ set isLocal = keyPieces[-2] == 'local' @@%}
{%@@ set remote_host = 'super-duper-dbg.host'
if not isLocal
else 'localhost' @@%}
{%@@set aws_db_port = 3333 @@%}
{%@@ elif keyPieces[-1] == 'dev' @@%}
{%@@set aws_db_host = 'localhost' @@%}
{%@@set aws_db_host = 4521 @@%}
{%@@ endif @@%}
{{@@ env(db_host, db_port) @@}}
Server1's environment file (projects/server1/.env)
{%@@ from projects/env import env @@%}
{{@@ env('average-host.com', 9632) @@}}
Part of Dotdrop config.yaml file:
# config.yaml
dotfiles:
server0-env-remote-dbg:
src: projects/server0/.env
dst: ~/projects/server0/.env.remote.dbg
server0-env-local-dbg:
src: projects/server0/.env
dst: ~/projects/server0/.env.local.dbg
server1-env:
src: projects/server1/.env
dst: ~/projects/server1/.env
With this configuration, installing the dotfile server0-env-local-dbg will
create an environment file in ~/projects/server0/.env.local.dbg with the
following content:
# .env
export DB_HOST='localhost'
export DB_PORT='3333'