Every company I've worked for had dozens of repositories written in the same tech stack (either due to a micro-services architecture, or just many different types of projects, think a few backend applications, a CLI app, an open source project, etc...).
What all of these repositories had in common was a project-level configuration that was almost the same. Some examples are:
.github/workflows/...
, .circleci/config.yaml
.vscode/
or .idea/
.eslintrc
, .prettierrc
, .golangci.yaml
, .pylintrc
, ...package.json
scripts, .github/dependabot.yaml
, .github/pull_request_template.md
, Makefile
And the list goes on...
Whenever I wanted to change such a configuration file, I had to open 10 or more pull requests in all of the different repositories, carefully copy-pasting the same changes, trying to stay focused (and sane) in the process to make sure I got everything changed correctly.
If we wanted to make 5 changes in 5 configuration files across 10 repositories, that would require 250 changes to be made just to update a configuration change!!!
One of the first principles we learn as programmers is
So how come we let ourselves rewrite the same configuration again and again in all of our projects?
Call me Sauron, but this drove me insane, and so I embarked on a quest to solve it.
I'm not the first to tackle this problem and approach solving it. While looking for solutions, I found 2 interesting projects. Here are my opinionated thoughts on both of them:
A powerful tool that uses tasks written in javascript to intelligently maintain updates on shared config files.
👍🏼 Pros:
👎🏼 Cons:
A
A
Cookie cutter is a tool that helps with creating new projects based on boilerplate templates.
👍🏼 Pros:
👎🏼 Cons:
When starting to think about a solution that would fit my needs, I had a couple of requirements in mind:
It balances simplicity and flexibility by using comments to annotate blocks of configuration that we would like to sync. I've felt the 2 alternative solutions required too much of me to get started.
Disclaimer: I'm the author of Goplicate
In the following simplified example, we'll sync an
We'll end up having the following folder structure (full example
+ shared-configs-repo/
+ .eslintrc.js
repo-1/
.eslintrc.js
+ .goplicate.yaml
repo-2/
.eslintrc.js
+ .goplicate.yaml
...
Let's go!
1️⃣ Choose a config file that some of its contents are copied across multiple projects and add goplicate block comments for the common-rules
section of your desire:
repo-1/.eslintrc.js
:
module.exports = {
"extends": "eslint:recommended",
"rules": {
+ // goplicate-start:common-rules
// enable additional rules
"indent": ["error", 2],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "double"],
"semi": ["error", "always"],
+ // goplicate-end:common-rules
// override configuration set by extending "eslint:recommended"
"no-empty": "warn",
"no-cond-assign": ["error", "always"],
}
}
2️⃣ Create a separate, centralized repository to manage all of the shared config files. We'll name it shared-configs-repo
. Then, add a .eslintrc.js
file with the common-rules
snippet that we want to sync:
shared-configs-repo/.eslint.js
:
module.exports = {
"rules": {
// goplicate-start:common-rules
// enable additional rules
"indent": ["error", 4],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "double"],
"semi": ["error", "always"],
// goplicate-end:common-rules
}
}
Goplicate snippets are simply the sections of the config file that we'd like to sync. In this example, we've also added the surrounding configuration to make it more readable, but it's not really needed.
3️⃣ Go back to the original project and create a .goplicate.yaml
file in your project root folder:
repo-1/.goplicate.yaml
:
targets:
- path: .eslintrc.js
source: ../shared-configs-repo/.eslintrc.js
4️⃣ Finally, run goplicate on the repository to sync any updates:
This is only a simplified example. Let's see Goplicate's additional features.
goplicate sync
command to sync multiple repositories with a single command.
goplicate sync
command to automate the opening of PRs for each update to the shared config repository.
Goplicate is in the early stages of development - If you want to contribute, then feel free to open a PR or hit me up @
Ilai Fallach
If you're maintaining many different projects with similar config files or a single project with many such repositories, then try
What do you think about Goplicate? Leave a comment and share your feedback and thoughts 🙏
Want to connect?
If you liked this content, consider signing up to my newsletter (it’s free).
Originally published at https://buildstupidstuff.com