DEV Community

Cover image for Upgrade NPM packages with GH Actions
Michael Currin
Michael Currin

Posted on

Upgrade NPM packages with GH Actions

My workflow

This post shows you how to use 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 competition. Read more and enter here.

Submission Category

Maintainer Must-Haves

Set up workflow YAML file

Here is what the workflow will do:

  1. Checkout the codebase.
  2. Set up Node v16 in the environment.
  3. 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.
  4. 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:

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)'
Enter fullscreen mode Exit fullscreen mode

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.

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.
  2. Select your workflow name. Workflows list
  3. Click the Run Workflow button. Run workflow
  4. 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 web app, which is built in Vue and Yarn.

Some relevant links around that repo for package upgrades:

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 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 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. And I enjoy using 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:

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

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

  • main.yml in my Node Project Template. This workflow tests and builds the app but does not deploy anything.
  • 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 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.

Image credits

Photo by Vlad Kutepov on Unsplash

Icons from github/explore repo.

Top comments (0)