4

Upgrading Prettier on a Large Codebase

 3 years ago
source link: https://flexport.engineering/upgrading-prettier-on-a-large-codebase-28d56c4de49e
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Upgrading Prettier on a Large Codebase

Image for post
Image for post

Prettier is excellent: it ensures everyone on your team uses consistent formatting. More importantly, prettier lets you reallocate brain cells once dedicated to mundane formatting decisions. The only downside is upgrading or using it for the first time on a large codebase can generate thousands of lines of changes that obfuscate git history. While it is impossible to totally remove the traces of prettier from git history (unless you’re willing to rewrite history), it is possible to mitigate some of the annoyances through a careful use of git, GitHub, and prettier.

Goals

After running prettier, we want the changes it makes to be attributed correctly in git and not create unnecessary merge conflicts in open pull requests. We also want the whole process to be easily repeated.

Attribution

While it is fun to pad your LOC changed stats with big automated changes, it causes headaches down the road, especially with git blame and other attempts to find a relevant code owner. You probably don’t want to be the last one to have “changed” a line of code in a file that is particularly temperamental. It’s much better for the automated changes to be attributed to the tool that made the changes rather than a specific developer that happened to run the tool.

Merge Conflict Resolution

Merge conflicts with other humans are annoying, but when conflicts are caused by a tool and possibly touch every file in a branch they can be infuriating. Also, the amount of time it takes to resolve conflicts manually for a whole team of engineers is not a cost worth incurring just to get some nicer formatting. We wanted to find a way to make as little of an impact on in-progress branches as possible.

Repeatability

Whatever solutions we came up with, we wanted to be able to upgrade prettier again in the future without having to redo a lot of manual work. It’s hard to remember exactly what set of commands you ran six months ago if they aren’t written down somewhere.

Solution

One approach to installing or upgrading prettier on a codebase is scorched earth: run prettier on everything, push a PR, and commit it to master. Unfortunately, this wouldn’t meet any of our goals. Instead, we developed a small set of scripts to handle upgrading prettier that are hopefully general enough to put to use on another codebase. These scripts are available for download at https://github.com/flexport/prettier-scripts.

Strategy

The general idea is to upgrade (or install) prettier, run it on every Javascript file in the code base, clean up any errors, make a commit to master (with attribution to prettier), and update all open pull requests. It would be nice if it were possible to make a single, no-interaction script for this process, but one of the steps in the process is manual. Prettier may move around semantically important comments (like // eslint-ignore and // $FlowFixMe) so that they no longer line up with the specific line of code they were applied to. Realigning these comments needs to be done by hand so that new errors aren’t accidentally introduced. Therefore, the script is split into upgrade.sh and finish-upgrade.sh. To avoid potentially losing changes, these scripts should be run without anyone else committing to master before the scripts finish (so it’s probably best to run them late at night or on a weekend).

upgrade.sh

This script does the actual upgrading of prettier as well as setting up some tagging in git so that we can upgrade in-progress branches later. This script makes only local changes. You might need to do a little manual cleanup if necessary. Once you’re happy with the changes, it’s time to push to master and all in-progress branches.

finish-upgrade.sh

This script commits and pushes the changes to the remote repository, as well as upgrading all branches in open pull requests on GitHub. Obviously you’ll want to change the email address. In order to interface with GitHub, you’ll need to set the environment variables GITHUB_KEY, GITHUB_OWNER, and GITHUB_REPO, explained below. Note: this script commits and pushes directly to master.

upgrade.sh and finish-upgrade.sh rely on some helper scripts to do a lot of the actual work. The first of these scripts is prettify-all.sh.

prettify-all.sh

This is a relatively simple script that runs prettier on all files in git and updates jest snapshots (an optional step that you can remove). By using git ls-files, we take advantage of ignoring files that git ignores. If you want to ignore additional files, you can use a .prettierignore file. Also, if you want to change the defaults of prettier, you can either add them to the command here or create a config file.

finish-upgrade.sh upgrades all branches with open pull requests on GitHub, which means we need a way to get all of the branch names from GitHub’s API.

get-branches.sh

This script should have probably been written in a language other than bash, but it was interesting to figure out how to do HereDocs and process JSON in bash. We use GitHub’s GraphQL API to get the information we need, which in this case is the branch name of open pull requests. The pull request query is paginated, which is why there is a loop in the script. You’ll need a personal access token from GitHub to set the GITHUB_KEY environment variable. The GITHUB_OWNER and GITHUB_REPO variables should be set as well.

After we have all the branches, we pass them into another script to actually upgrade them.

upgrade-branches.sh

This script is just a simple loop that tries to upgrade every branch, ignoring failures. The real work is done in the upgrade-branch.sh script.

upgrade-branch.sh

This script attempts to rebase the branch onto the pre-prettier tagged commit. This is the point in history where the new version of prettier is installed but not yet run. This way the current branch can install the new version of prettier and run it on all changed files. Next, it can rebase against the post-prettier tag to pick up all of the prettier changes. Now, the branch doesn’t conflict with master and can be merged cleanly. If the branch has conflicts and cannot be rebased onto pre-prettier, the branch author can fix the conflicts and re-run this script separately to upgrade their branch.

The final step of upgrading is to clean up some of the tags we made, so that it’s possible to reuse them in the future.

cleanup.sh

This script creates tags with dates for the upgrade, and then deletes the rest of the tags. You’ll want to make sure none of the branches are in a bad state somehow, because this will delete the backup tags. It’s probably best to run this a few days after the upgrade. If you want to keep the backups, just remove the last line of the script.

Conclusion

Using a set of scripts to do a large, but automatic, code change instead of doing it manually achieves all of our goals set out at the beginning of this process. We can now stay up-to-date with the latest version of prettier without forcing every engineer with an open pull request to deal with merge conflicts.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK