A Github Action to bump your cabal dependencies

I created a Github Action that (regularly or when triggered manually)

  1. Checks out your repository
  2. Uses cabal bounds to recognize bumpable dependencies
  3. Build and tests (cabal build && cabal test), forcing the use of that dependency with --allow-newer and --constraint
  4. Update the cabal file accordingly
  5. Create a PR (or update an existing PR)

Step three is crucial, becase with the typical Haskell CI setup, a PR that just bumps the dependency is not enough:

Imagine your package foo depends on bar < 1.2 and baz. Now bar-1.2 is released, and someone (or something) creates a PR against your repository changing the version bound to bar < 1.3. Your CI runs the usual set of tests, and turns :green_circle:. You merge the PR. All well?

No! If baz happens to depend on bar < 1.2 and no new version is available yet, your CI still silently used the old version of bar!

This is why this action test the compatibility with precisely that new version.

(There may be ways to have your existing CI perform such logic, see this haskell-ci issue. Then the PR creation would be much simpler, and could even be delegated to tools like dependabot or renovate. But I expect we don’t have that in most cases).

See this README section for more answers to possible questions about this tool.

This is very new, just created with @andreasabel during MuniHac, so grab it now and enjoy finding problems before someone else does!

23 Likes

In your opinion, what can cabal-install do to make writing this kind of tools easier?

Have flags to declarative say “I want to test my upper bounds” etc would already help, as it would make it easier for people to configure their CI in a way that it can reasonably test PRs that bump dependencies. See this cabal issue

1 Like

Well, that’s specific to the particular problem you solved. I meant to ask in a more general way.

E.g. I only had a quick look but you seem to have to manually parse the output of cabal outdated so maybe outputting json (where appropriate ofc) could help?

Or maybe even cabal outdated should be a separate utility can be written on top of cabal-install functionality?

I don’t know. I love that you made this and I wonder how we can get more tools like this :blush:

A JSON output for cabal outdated would be appreciated, but isn’t the main hurdle. The interesting part is step three: given a proposed change to the build depends ranger, test against the newly allowed version.

That’s non-trivial to implement (and in less simple cases maybe impossible), but it is also crucial if you want a CI setup that can assess PRs that propose to extend version ranges.

2 Likes

Thank you Joachim! :relaxed:

3 Likes

Would it be possible to use the actions/checkout@v3 myself instead of having your action do it? Because for tzdata, I need to download some stuff before the package can be built.

Seems to me that it is more modular to use a smaller composite action if possible. The example could still include the checkout.

Yes, that would probably be possible. I just don’t know where the right balance between “this is simple and convenient for most” and “this is too flexible and no longer simple” is. Annoying non-compositional non-higher-order systems…

Or maybe the default should be like this, and then we add a few inputs to tweak behavior (no_checkout etc.). Is that better or worse?

1 Like

I am starting to lean towards scrapping this approach, where the bump action tries to build the package before creating a PR, and towards a model where

  • PRs bumping the dependencies are created without actually testing them
  • The regular CI is tasked with testing whether they are good.

This way:

  • The bump action doesn’t have to know how to test the package, which may vary a lot between projects.
  • There is no ambiguity if the regular bump action should fail loudly or silently if the package doesn’t build.
  • The open PRs provide a good overview of outdated dependencies that need to be handled.

A pre-requisite for this model is that the CI actively tests the upper bound, which most Haskell CI setups so far do not do. But it is possible, and I have experimented a bit in that direction:

Until then, it is possible to integrate into hand-written CI setups, as I did here:

Those of you who have played with the bumping action (or who didn’t play with it, but thought about this), what do you think of this?

2 Likes

Could it call an optional reusable workflow if specified? see Reusing workflows - GitHub Docs

Oh, I didn’t know about reusable workflows. Pretty neat, I could package a multi-job CI setup that way.

I am not sure if a reusable workflow for bumping the bounds can “call back” to the repo’s custom CI workflow (higher-order reusable workflows…). It’d also have to pass extra flags…

but it seems simpler to just make sure that the normal CI is “good enough” – after all, you want a good CI also for dependency-bumping PRs that may have been created manually!

The haskell-bounds-bump-action now has an option to not run cabal build && cabal test before creating the PR, so it can be used in two modes:

  • Test before opening PRs (easy, not customizable)
  • Always open PR, test using regular CI (a bit more work to set up, but better™ in a few ways).

The README explains the differences a bit.

I wonder if I should change this from a reusabe action to a reusable workflow. Not sure if it makes a big difference; I guess the log output would be a bit prettier, showing each step separately.

1 Like