Skip to main content

Command Palette

Search for a command to run...

Automate Flutter package upgrades with GitHub Actions

A Dependabot alternative

Published
5 min read

Like with pretty much any other framework, keeping up with package upgrades in Flutter can get pretty tedious. I decided to see if I could make things a little less tedious over the weekend and I'd like to share a GitHub Action workflow that I came up with while experimenting.

What about Dependabot?

I'd actually be happy to use Dependabot but there isn't full support yet. Somebody is working on it at the moment though. I'm confident there will be full support one day, it's just a matter of time. For now though Dependabot isn't really a realistic option.

Using GitHub Actions

There was the option of creating an Action and putting it on the Actions Marketplace but I'm not entirely sure what that would involve yet and I'm not that committed to the idea yet either considering that there is work being done on the Dependabot front. So in the end I just decided to create a workflow with existing Marketplace Actions. Here's roughly the set of steps I wanted the workflow to use:

  1. Check out the repo
  2. Pull in cache data(so the next step runs a little faster)
  3. Setup Flutter CLI so it can be used in the next steps
  4. Run flutter pub upgrade --major-versions
  5. Run flutter pub get
  6. Run flutter test
  7. (Optional) Capture the output of flutter pub outdated for later
  8. If there are changes from (4) commit them to a new branch and open a PR

And obviously I'd want this workflow to run every so often so it would need to be scheduled with cron. In the end this is what I came up with:

name: Upgrade packages

on:
  workflow_dispatch:
  schedule:
    - cron: "0 0 * * *"

env:
  flutter_version: "2.2.2"

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  upgrade_packages:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2
      - name: Cache Flutter dependencies
        uses: actions/cache@v2
        with:
          path: /opt/hostedtoolcache/flutter
          key: ${{ runner.OS }}-flutter-install-cache-${{ env.flutter_version }}  
      # Installs Flutter    
      - uses: subosito/flutter-action@v1
        with:
          flutter-version: ${{ env.flutter_version }}
          channel: stable   
      # Upgrade packages(bump major versions)    
      - name: Run upgrade
        run: flutter pub upgrade --major-versions
      # Get/install packages  
      - name: Run get
        run: flutter pub get  
      # Run tests  
      - name: Run tests
        run: flutter test
      # Captures the output of `flutter pub outdated`  
      - name: Check remaining outdated packages
        id: check_outdated
        run: |
          content="$(flutter pub outdated)"
          content="${content//'%'/'%25'}"
          content="${content//$'\n'/'%0A'}"
          content="${content//$'\r'/'%0D'}"
          echo "::set-output name=outdated_info::$content"
      # Opens a pull request with changes    
      - name: Create Pull Request
        uses: peter-evans/create-pull-request@v3
        with:
            token: ${{ secrets.PAT }}
            commit-message: Upgrade packages
            title: Upgrade packages
            body: | 
              Output for outdated check following upgrades:
              ${{ steps.check_outdated.outputs.outdated_info }}
            branch: feature/update-packages

For this to work you need to generate a Personal Access Token in your GitHub account's settings and then add that as a secret to your repository. You can learn more about this here. This is what the ${{ secrets.PAT }} value represents in the last step.

You might have also noticed that I have this set to run everyday at 12am UTC. I'm going to tweak this later on but like I said I'm just experimenting with things at the moment and want to see if I run into any issues down the road. If I find some improvements I can make I will update this post. If you wanted to run this on a weekly schedule instead, you could use something like "0 0 Sun" instead(12am UTC every Sunday).

Caveats

The above workflow has been working pretty good for me for the last few days on some simple, small projects. That being said it is far from perfect.

First of all the above workflow won't upgrade your Pods on the iOS side of things. If you switch from ubuntu-latest to macos-latest you could run pod install --repo-update in the ios directory of your project, I did try this out personally and it seemed to work okay - but keep in mind that workflows that use MacOS cost something like 10 times more than workflows that use Ubuntu. This would probably matter less if you were only running the workflow once a week or something like that.

Another thing you could tweak is the flutter pub upgrade --major-versions command. This command will potentially pull in breaking changes because it will upgrade over major versions. You can opt out of this behaviour by just running flutter pub upgrade instead.

One more shortcoming of the workflow above is that it only runs your tests. Your tests might not cover everything so adding a flutter build ios or flutter build apk might add an extra layer of security if the upgrades break your project. I think I'm going to look into all of these points as I keep tweaking the workflow.

Summing up

Like I said the workflow I introduced above definitely isn't perfect but I think that it's a good start and something that can be used to keep sane while we all wait for Dependabot support. If you're interested in more Flutter tips, I wrote another post about adding linting to a project a couple of days ago - you can find it here.

R

Just curious, what is your reasoning for wanting to automate this, packages are one of the few things that should be managed manually. The risk for breaking changes is pretty high, even in a well-tested application.

The problem is many developers do not understand the purpose or meaning of semantic versioning and I have personally seen breaking changes come out with patch and even build releases, developers rewriting entire API's and publishing as a patch release, and this was not even on pre-V1 releases.

One package, I recall 2.3.3 was working perfect and 2.3.3+1 had a new API. lol.

While in theory this is a good idea, not sure it's something one ever wants to put into practice back in the real world.

J

Thanks for your comment!

I guess the core of my answer is just that I’m lazy 😂. I got sick of manually upgrading the packages in my projects. Most of the upgrades for stuff like url_launcher for example are minor but it gets updated somewhat often.

I get what you mean about semantic versioning though, some people don’t follow it and you get issues once in awhile. For a project in production that has a lot of users I would leave out the major version flag. There is definitely risk involved with breaking things but I feel like it is worth it personally.

To be honest the idea of things breaking did horrify me a bit at first but I thought I’d try it out anyway and I’m slowly warming to it. I’ve set my own Actions to run once a week and I haven’t had any major problems yet(we’ll see how things go though…). There are still projects I have that I upgrade manually. Opinions are definitely divided but there must be demand for it considering things like Dependabot exist. 😅

R

Jordan Holland

Agreed, but Dependabot exists purely for security purposes and is an alerting tool first and foremost.

It's not often that it automatically creates the PR for the fix and in the end, you still need to approve the merge so it leaves the decision 100% in the hands of the developer, it does not make the choices for you.

Personally use the IDE plugin to inform me when package updates are available, I can then open the spec and just click "accept". That way the analyzer has a heart attack if there are breaking changes and I can simply "ctrl+z" and carry on with my life until I feel like dealing with it.

J

Reme Le Hane I'm not sure if I agree with that. If you go on the homepage it says 'Automated dependency updates' and below that it says 'Dependabot creates pull requests to keep your dependencies secure and up-to-date.' then if you look at the the 'How it works' flow it says 'Dependabot pulls down your dependency files and looks for any outdated or insecure requirements.' then 'If any of your dependencies are out-of-date, Dependabot opens individual pull requests to update each one.' (https://dependabot.com/)

Security is definitely a part of it but I'd say the automated dependency updates are just as important. Maybe it started out with a security focus, but there are now even StackOverflow questions about how to configure it just to do security updates(https://stackoverflow.com/a/64145646) (apparently you don't even need to configure it in your repo anymore since GitHub acquired it, it just does it automatically for security updates now and I've seen it do this in some of my NodeJS/Javascript repos).

I'm not saying what I wrote above is a replacement or better than Dependabot, it is way too basic. If there was proper Dependabot support I would just use that(fingers crossed it happens soon 😅).

What you said about PRs, the same thing applies to what I wrote above in the action - you need to merge the changes for them to take affect it doesn't auto merge the PR for you it just creates it. You have to make a conscious decision to merge it.

The big difference is it doesn't update things one by one like Dependabot or have the same level of granular settings. If you are worried about the project breaking I think it'd just be a matter of running flutter build apk like I wrote above and it would tell you if you had any issues(then the action would fail, same as if your tests failed - the PR wouldn't even be created).

As far as the IDE plugin goes that actually sounds pretty nice, what are you using Android Studio or Visual Studio Code?

More from this blog

DeveloperMemos

6 posts