5 tools to automate your development
Tags: productivity, github, devops
Published: 2021-08-27 12:43:48

Automating your development with Dependabot, Auto Assign, Merge Freeze, Husky and Scheduled reminders.

The idea of this post is to introduce some of the tools and integrations that made our development life easier.

Most of these are pretty straightforward to implement into your workflow but for the ones that have a couple of gotchas I might write an extended introductory version for that tool alone.

## 1. Dependabot
### *Automated dependency updates*
*Dependabot creates pull requests to keep your dependencies secure and up-to-date.*

[Dependabot](https://dependabot.com/) is dead simple and their punchline clearly states what it does. We started using it a couple of years back, a bit before [Github acquired it](https://dependabot.com/blog/hello-github/).

The main reason was that, at the time our current team took over the Front End division, there were a lot of outdated dependencies which we wanted to update and wanted to keep up to date. We found Dependabot, added it to our projects, and let it do its magic ever since.

Now it's natively a part of Github, so adding it is even easier than before.  You can [check out](https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically) how to set up Dependabot, but in the end you'll end up with a `dependabot.yml` in your `.github` folder.
Ours looks like this:

``` yml
version: 2
updates:
  - package-ecosystem: "npm" # See documentation for possible values
    directory: "/" # Location of package manifests
    schedule:
      interval: "daily"
    open-pull-requests-limit: 2
    commit-message:
       prefix: "BleedingEdge"
```
The only thing that differs from the default settings is that we:
- chose npm as our package-ecosystem
- limited the number of open PRs to 2
- added a prefix for the default Dependabots commit message (you'll see later why)

Four years back we had 3 frontend repos, now we have around 14 active ones. Manually keeping every dependency up to date would be extremely time consuming. Dependabot helps a lot, but it still takes time to review and merge all the PR's. We usually take a day, after our weekly release, to merge Dependabots pull requests.

*Writing this got me wondering if we could set the bot to open PR's only on major, minor or patch versions and indeed the feature [was requested in 2018](https://github.com/dependabot/dependabot-core/issues/2219) and got released a few months ago and now you can ignore SemVer updates of your choice. Check out [GitHub's blog post](https://github.blog/changelog/2021-05-21-dependabot-version-updates-can-now-ignore-major-minor-patch-releases/) for more.*


## 2. Auto Assign
### *Add reviewers/assignees to pull requests when pull requests are opened.*

So you opened a pull request, you need at least two approvals and you need to add reviewers to your pull request. Every single time.

Things are pretty obvious, let the bot do it. 

Setting up is easy, go to [probot.github.io/apps/auto-assign](https://probot.github.io/apps/auto-assign/) hit the Add to Github button and don't ever worry about manually adding reviewers!
Similar to Dependabot, you will end up with a `auto_assing.yml` file:

```yml
# Set to true to add reviewers to pull requests
addReviewers: true

# Set to true to add assignees to pull requests
addAssignees: false

# A list of reviewers to be added to pull requests (GitHub user name)
reviewers:
  - teammember1
  - teammember2
  - teammember3
  - ...


# A number of reviewers added to the pull request
# Set 0 to add all the reviewers (default: 0)
numberOfReviewers: 0

# A list of keywords to be skipped the process that add reviewers if pull requests include it
skipKeywords:
  - BleedingEdge

```
We have nothing out of the ordinary here. We are utilizing the *skipKeywords* option with the *BleedingEdge* keyword that if you remember Dependabot prefixes to each of its pull requests. We handle the pull requests Dependabot opens a little bit differently and don't want to burden all the reviewers with them.

Once the Pull request is opened the bot kicks in and you see it in the timeline requesting reviews:
![Auto assign bot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4ke3agursj98fenqcs28.png)

*You can also try and use the default settings Github provides for Code Review Assignments. Just go to your team page, in the top right hit Settings and you'll find the Code review assignments tab. We tried it but it didn't work out for us.*

## 3. Merge Freeze
### *The code freeze tool to block merging and deployments*

The reason for adding merge freeze originated from a simple question:

> *Can you tell all the developers to stop merging since we are starting regression?* 

We could announce it in our team's channel in hopes that everyone reads the message in time. Or we could integrate a tool that allows the QA team to issue a command on Slack that freezes/unfreezes merging to the repository. [Merge Freeze](https://www.mergefreeze.com/) to the rescue.

Again, setting up, nothing too complex. What we did find Merge Freeze is missing is the ability to bulk freeze. It worked well when we needed to freeze a couple of repos. But once the number of our repositories increased to over 10, manually entering the command more than 10 times... you get it.

For this we used [Slack Apps](https://slack.com/apps) and [AWS Lambda](https://aws.amazon.com/lambda/).

We created a custom Slack App for our workspace called Deployment, that has two Slash commands: `/freeze_all` and `/unfreeze_all`. Both commands have the Request URL set to our Lambda url, and pass the freeze value as a query parameter: `?freeze=true | false`.

Using it on Slack looks like this:

![Slack Freeze command](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/79wp9ch4ok8h3fn00h6o.png)

The Merge Freeze tool exposes an API endpoint for each repository you add to it, which you can use to freeze or unfreeze it. That makes the Lambda rather simple, it just makes a POST request to each of the endpoints provided by MergeFreeze.


```javascript
const https = require('https');

exports.handler = async (event) => {

  const freezeValue = getFreezeValue(event);
  if (!freezeValue) {
    return {
      statusCode: 400,
      body: JSON.stringify('BOOM! You need to provide a freeze value as a query param, either true or false'),
    };
  }

  const userName = getUserName(event)

  const baseOptions = {
    hostname: 'mergefreeze.com',
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Content-Length': 0,
    },
  };

  const appOneOptions = {
    path: `/api/branches/your-organization/your-repo/main/?access_token=${process.env.ACCESS_TOKEN}&frozen=${freezeValue}&user_name=${userName}`,
    ...baseOptions,
  };
  ...
  /** Removed the rest of declaration to keep the preview short */
  

  await Promise.all([
    doRequest(appOneOptions),
    ...
    doRequest(appElevenOptions),
  ]);

  console.log("I'm done with all your promises!");

  // Text that gets return to Slack that is only visible to the person entering the command
  return {
    statusCode: 200,
    body: JSON.stringify('You have such power!!!'),
  };

  function doRequest(options) {
    return new Promise((resolve, reject) => {
      const req = https.request(options, (res) => {
        res.setEncoding('utf8');
        let responseBody = '';
        res.on('data', (chunk) => responseBody += chunk);
        res.on('end', () => resolve(responseBody));
      });

      req.on('error', (err) => reject(err));
      req.end();
    });
  }

  function getFreezeValue(event) {
    let freezeQueryString;
    let freeze;

    if (event && event.queryStringParameters && event.queryStringParameters.freeze) {
      freezeQueryString = event.queryStringParameters.freeze;
    }

    if (freezeQueryString === 'true' || freezeQueryString === 'false') {
        freeze = freezeQueryString;
    }

    return freeze;
  }

  function getUserName(event) {
    const bodyQueryParams = new URLSearchParams(event.body);
    return bodyQueryParams.get('user_name') || 'Web API';     
  }
};
```
After entering the command, MergeFreeze lists all the repos that got frozen or unfrozen and you get a confirmation message from Slack, making your work day a bit better!

![Much Power](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1jwu3j6pjccg1clsw15y.png)

After the regression is done, everything is pushed to Production and is smoke tested, the lead QA issues the `unfreeze_all` command and life goes on.




## 4. Husky
### *Modern native Git hooks made easy*

We use Jira as our work management tool so we have to prepend the ticket ID to our branch names and commit messages in order to utilize both the Development panel when viewing an issue and VSCodes extension GitLens:

Jira ticket Development panel:
![Jira Development Panel](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2wgvumpwofinjccdlsak.png)

This means that each time you create a branch you have to remember to include the Jira issue ID, eg: task-**ND-123**-add-authentication. That alone was not a big deal as it quickly became a habit. But what was a PIA was prepending it to every commit message. The 1st automation round was just setting up the git `prepare-commit-message` hook on your local machine, but as the team grew larger we needed a better solution which Husky provided!

[Husky](https://typicode.github.io/husky) in combination with [jira-prepare-commit-msg](https://github.com/bk201-/jira-prepare-commit-msg) is what worked for us:

```json
...
  "private": true,
  "husky": {
    "hooks": {
      "prepare-commit-msg": "jira-prepare-commit-msg"
    }
  },
  "jira-prepare-commit-msg": {
    "messagePattern": "$J $M",
    "jiraTicketPattern": "(\\w+-\\w+-\\d+)"
  },
  "dependencies": {
...
  "devDependencies": {
    "husky": "^4.3.8",
    "jira-prepare-commit-msg": "^1.5.2",
...
```
The JIRA ticket ID is taken from a git branch name. Now you could just write `commit -m "Fixing a typo"` and you would get a commit message looking like *task-ND-123-Fixing a typo*.

In case you did not name your branch correctly eg: missing Jira ticket ID you would get an error:

``` bash
my-application git:(main) ✗ git commit -m "Add authentication methods"
husky > prepare-commit-msg (node v14.15.0)
JIRA prepare commit msg > start
JIRA prepare commit msg > Error: The JIRA ticket ID not found
JIRA prepare commit msg > done
```

This came in nicely as everything was set up in `package.json` and a new developer would do `npm i` and she/he is pretty much set, no need to manually configure hooks.

But the Jira Ticket ID in the commit message in combination with GitLens is what really made this super useful. 

GitLens with Git blame annotations:
![Visual Studio Code and GitLens](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ig9br67fr317myzh0sed.png)

There were a lot of times where we had to, for various reasons, open up and read the Jira issue associated with the code change. Having the ticket ID at each mouse click throughout the codebase saved us a lot of time. (opening it in the browser is easy as well, just take the Jira ID and add it after *.../browse/* + *ND-123* e.g., https://your-organization.atlassian.net/browse/ND-123)

GitLens is a cool tool which I personally use on a daily basis. It helps you to visualize code authorship at a glance via Git blame annotations and code lens. You can also very easily navigate back in history to see past commits which is useful as well.



## 5. Scheduled reminders for Pull Requests
### *Scheduled reminders help teams focus on the most important review requests*

This is something we added not too long ago, just a bit after we moved to Micro Frontend Architecture. One of the reasons for adding it was that the number of repositories increased from 4 to 14 so having a dedicated channel for open pull requests made sense. Prior to this we would post a link of the PR in our team's main channel, or would hope people would see it in their email. This way we moved the noise to a dedicated channel, and the devs know the team will get automatically notified.


![GitHub Scheduled Reminders](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/75ike2bcdg4sh4eossx1.png)

We get notifications on every working day each full hour from 8-16. It ignores approved pull requests (in our case when 2+ ppl approved it) and we also have ignore terms for *BleedingEdge* so it ignores pull requests opened by Dependabot.

Setting up [scheduled reminders](https://docs.github.com/en/organizations/organizing-members-into-teams/managing-scheduled-reminders-for-your-team) is straightforward and you can find the github docs [here](https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/managing-your-membership-in-organizations/managing-your-scheduled-reminders). This is how it looks like once its up, in our case it posts messages in a private *frontend-pull-requests* channel:

![FrontEnd Pull Requests](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3i1gnp3rl00oev5yvf6r.png)

...


There are a lot of improvements that we could include on top of what we have, like creating branches directly from Jira would ease up on having to remember the naming convention. Or maybe we could have chosen a merge freeze tool that has bulk freeze built in. But usually we had limited time for investigation, or it was good enough at that time, and later on we just tried to improve the process instead of replacing the tool. 
If you have any suggestions, please do post them in the discussion below! 

***

Feel free to connect 👋

[Twitter](https://twitter.com/pgarzina) | [Instagram](https://www.instagram.com/pgarzina/) | [LinkedIn](https://www.linkedin.com/in/petar-garzina/)