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-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.
Here is what the workflow will do:
- Checkout the codebase.
- Set up Node v16 in the environment.
- Upgrade packages with
npm updateas a shell command. If there is anything that got upgraded, then
package-lock.jsonwill be updated.
- 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 -
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.
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.
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:
- Go to the Actions tab of your repo.
- Select your workflow name.
- Click the Run Workflow button.
- 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.
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:
- upgrade-packages.yml workflow config.
- Upgrade NPM Packages runs - runs under Actions, to see log output.
- Automated PRs 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.
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-requestrepo 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.
Some more info and context about why I made this workflow.
- The upgrades will only be within semantic versioning restrictions of your
package.jsonfile. For example, a major upgrade from
v2.X.Xwon't happen if you specify your versions as
"^1.2.3"or similar in your
package.jsonfile. 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.
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.4.X, or from
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!
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.
I did a write-up here on a workflow, including how to use Yarn and upgrade major versions:
- Upgrade Packages workflow.
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.
- GH Actions workflows in Code Cookbook
Icons from github/explore repo.