Upgrade NPM packages with GH Actions
Tags: actionshackathon21, javascript, npm, githubactions
Published: 2021-12-03 10:46:15

## My workflow

This post shows you how to use [GitHub Actions](https://michaelcurrin.github.io/dev-resources/resources/ci-cd/github-actions/) for automated NPM package upgrades. The workflow willupgrade your app's NPM packages described in `package.json` and `package-lock.json` and then create an automated PR if any changes were committed.

This flow can be scheduled (such as weekly) or triggered on a button press. All without having to touch your local CLI.

This post is an entry into the DEV.to [actionshackathon21](https://dev.to/t/actionshackathon21) competition. Read more and enter [here](https://dev.to/devteam/join-us-for-the-2021-github-actions-hackathon-on-dev-4hn4).

## Submission Category

**Maintainer Must-Haves**


## Set up workflow YAML file

Here is what the workflow will do:

1. Checkout the codebase.
1. Set up Node v16 in the environment.
1. Upgrade packages with `npm update` as a shell command. If there is anything that got upgraded, then `package.json` and `package-lock.json` will be updated.
1. The final step will create a commit and a Pull Request, acting as a GitHub Actions bot account. Or do nothing if there are no changes to commit.

To set up, create this file in your repo - `.github/workflows/upgrade-packages.yml`.

Add contents:

```yaml
name: Upgrade NPM packages

on:
  workflow_dispatch:

  schedule:
    - cron:  "0 0 * * 0"
    
jobs:
  upgrade-packages:
    name: Upgrade packages

    runs-on: ubuntu-latest

    steps:
      - name: Checkout 🛎️
        uses: actions/checkout@v2

      - name: Set up Node.js ⚙️
        uses: actions/setup-node@v2
        with:
          node-version: '16.x'

      - name: Upgrade packages 🔀
        run: npm update

      - name: Commit and create PR 🔀
        uses: peter-evans/create-pull-request@v3
        with:
          title: 'build(deps): Upgrade NPM packages (automated)'
          branch: 'build-deps-upgrade-npm-packages-automated'
          commit-message: 'build(deps): upgrade NPM packages (automated)'
```

Then commit it.

Pro tip - if you go to the _Actions_ tab and create a new workflow from there using a template, then you'll get a neat editor view in GitHub which gives you hints and auto-complete specify for GH Actions.

For the final step above, you can check out the [Create Pull Request][] action on Actions Marketplace. Note that is used a workflow-scoped token internally, so you don't have to specify any token.

[Create Pull Request]: https://github.com/marketplace/actions/create-pull-request

If you are not using NPM, you can easily swap some steps. For Yarn, you will already get Yarn in your environment so just make the shell command `yarn upgrade`. For Ruby, use `setup-ruby` Action and `bundle update` as the shell command.


## Run your workflow

You can wait until midnight on Sunday comes around. You'll get a notification in your GitHub account that the PR was created. 

Or trigger the workflow manually:

1. Go to the _Actions_ tab of your repo.
1. Select your workflow name.
   ![Workflows list](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9xewx0e8jbvjdf30tun1.png)
1. Click the _Run Workflow_ button.
    ![Run workflow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/14ofi6u1vr7o1bb9j48z.png)
1. Then run with it with default settings.

Check that the workflow runs fine and has no errors.

Then go to the _Pull Request_ tab to find the PR. Check it looks good and then approve and merge it.

For confidence in the stability of the app, you can also run CI checks in the upgrade workflow or your standard build-and-test workflow. More on that in the sections below.


## Example of the workflow in use

I use a workflow similar to the one above that is implemented in my [Badge Generator](https://github.com/MichaelCurrin/badge-generator) web app, which is built in Vue and Yarn.

Some relevant links around that repo for package upgrades:

- [upgrade-packages.yml](https://github.com/MichaelCurrin/badge-generator/blob/master/.github/workflows/upgrade- packages.yml) workflow config.
- [Upgrade NPM Packages runs](https://github.com/MichaelCurrin/badge-generator/actions/workflows/upgrade-packages.yml) - runs under _Actions_, to see log output.
- [Automated PRs](https://github.com/MichaelCurrin/badge-generator/pulls?q=is%3Apr+automated) that exist in the repo.

Compared with the YAML snippet in this post, my linked workflow uses a more complex steps and GitHub Actions syntax.

Here are some notable differences:

- It _checks_ if packages are outdated, and if there is nothing to update, it skips other steps.
- The packages to upgrade are logged before any changes are made to the lockfile.
- The workflow runs lint, test, and build steps to ensure the app is working fine against the new set of packages. If a step fails, the workflow will give an error and the PR will **not** be created.


## CI against PRs

For typical development (not upgrading packages), I recommend having some CI against your PRs for quality control. So that you test and build the app with GH Actions _before_ you merge a PR.

Unfortunately, due to security limitations, the automated PR that your new upgrade workflow generates will _not_ have GH Actions checks run against it. See [issue](https://github.com/peter-evans/create-pull-request/issues/48) on the Create PR action discussing this.

I found workarounds:

- On the generated PR's branch, I make a non-functional whitespace change in `package-lock.json`. And because is change was made by a human and not a bot, that kicks off the workflow run. 😃 
- There's a suggestion comment on issue [#48](https://github.com/peter-evans/create-pull-request/issues/48#issuecomment-536204092) of the `create-pull-request` repo which involves creating a custom token.
- You can also delete and make a new PR with the existing branch, but I prefer not to do that.

Let me know if you have any other ideas. 

Maybe GitHub will add a feature to "Approve and Run" such a generated PR? This is already an option in the case of a PR created from a _fork_, but doesn't help me here.

Anyway, that limitation is okay I think. If you decide to add test and build steps in the actual upgrade workflow, the PR will only get created if everything passes, increasing confidence that the automated PR has good quality even if you don't check it directly.


## About

Some more info and context about why I made this workflow.

### How can I be confident nothing broke?

- The upgrades will only be _within_ semantic versioning restrictions of your `package.json` file. For example, a major upgrade from `v1.X.X` to `v2.X.X` won't happen if you specify your versions as `"^1.2.3"` or similar in your `package.json` file. If you want to _do_ that kind of upgrade, you should probably do that manually and locally, to avoid risk.
- I recommend adding _test_ and _build_ steps to your CI so you have confidence that the newer package versions don't break your app, and only merge the PR once you're happy.

### Why automate this?

For context and reasons for this flow. 

I have a couple of Node-based projects on GitHub. I'm using modern packages where possible when making a new project, but, the packages or sub-dependencies still become out of date easily as newer versions are released as minor or patch versions e.g. from `v2.3.X` to `v2.4.X`, or from `v2.3.4` to `v2.3.5`.

I would like to get any bug fixes, performance enhancements, and security patches. But without having to remember to take the time to manually upgrade packages locally with the CLI, test, build, and commit and push. That can all be automated!

### What about security scanning?

GitHub already supports [code scanning for vulnerabilities](https://docs.github.com/en/code-security/supply-chain-security/managing-vulnerabilities-in-your-projects-dependencies). And I enjoy using [snyk.io](https://snyk.io/) as a similar external tool. 

You should continue to use those tools to patch vulnerabilities with generated PRs. But, by using my workflow, you'll be upgrading your packages so frequently that sometimes you'll get your app upgraded to secure packages even _before_ the scans tell you the old version is insecure.

If you have a bunch of vulnerabilities come up in GH or Snyk, you can try run your upgrade packages workflow manually and merge the PR. Maybe the vulnerabilities will be resolved like that.


## Resources

I did a write-up here on a workflow, including how to use Yarn and upgrade major versions:

- [Upgrade Packages](https://michaelcurrin.github.io/code-cookbook/recipes/ci-cd/github-actions/workflows/node/upgrade-packages.html) workflow.

I have a popular post for if you are new to using Actions:

- [Beginner's guide to GitHub Actions](https://dev.to/michaelcurrin/beginner-s-guide-to-github-actions-49aa).

Here are some specific GH Actions workflows to recommend on standard CI around building and testing a Node app.

- [main.yml](https://github.com/MichaelCurrin/node-project-template/blob/master/.github/workflows/main.yml) in my Node Project Template. This workflow tests and builds the app but does not deploy anything.
- [main.yml](https://github.com/MichaelCurrin/react-quickstart/blob/main/.github/workflows/main.yml) in my React Quickstart to test and build the app and commit the build output to GitHub Actions, to be served as a GH Pages site.
[main.yml](https://github.com/MichaelCurrin/badge-generator/blob/master/.github/workflows/main.yml) in my Badge Generator, using Yarn. This is a GH Pages site too.

There are a ton of GH Actions I've written and collected, covering Node, Python, GH Pages, Rust, and more. Including multiple ways to solve the same problem.

- [GH Actions workflows](https://michaelcurrin.github.io/code-cookbook/recipes/ci-cd/github-actions/workflows/) in Code Cookbook



## Image credits

Photo by <a href="https://unsplash.com/@kvtepov?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Vlad Kutepov</a> on <a href="https://unsplash.com/s/photos/up?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
  
Icons from [github/explore](https://github.com/github/explore/tree/main/topics) repo.