DIY Dependabot for Clojure
Thu, Jan 28, 2021Unfortunately, dependabot does not support Clojure. And as such, I came up with a silly idea of putting something similar together using Github Actions.
First things first here is the complete Github Actions workflow:
name: Dependencies
on:
schedule:
- cron: '0 */3 * * *'
workflow_dispatch:
jobs:
bump:
runs-on: ubuntu-latest
container:
image: nixos/nix
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- run: nix-env -i clojure
- name: run depot
run: |
clojure -Sdeps '{:aliases {:outdated {:replace-deps {olical/depot {:mvn/version "2.1.0"}}}}}' -M:outdated -m depot.outdated.main --every --write > deps-output.txt
- uses: actions/upload-artifact@v2
with:
name: deps-output
path: deps-output.txt
- uses: actions/upload-artifact@v2
with:
name: deps
path: deps.edn
open-pr:
runs-on: ubuntu-latest
needs: bump
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/download-artifact@v2
with:
name: deps-output
- uses: actions/download-artifact@v2
with:
name: deps
- id: get-pr-body
run: |
body=$(cat deps-output.txt)
echo ::set-output name=body::$body
- name: create PR
id: cpr
uses: peter-evans/create-pull-request@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
title: "[Automated] Update dependencies"
body: ${{ steps.get-pr-body.outputs.body }}
commit-message: "[Automated] Update dependencies"
branch: update-dependencies
labels: |
dependencies
- name: check outputs
run: |
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
- uses: geekyeggo/delete-artifact@v1
with:
name: deps-output
- uses: geekyeggo/delete-artifact@v1
with:
name: deps
And now lets step by step to see what is going on here.
First, we allow workflow to be scheduled manually for testing purposes and via cron schedule every 3 hours:
on:
schedule:
- cron: '0 */3 * * *'
workflow_dispatch:
After that, we define our first job.
Workflow is split into two pieces because peter-evans/create-pull-request@v3
has some issues running in custom containers (based on my experience).
And because of rlwrap
issue in docker.
If you are able to run clojure clj tool in ubuntu image directly this config can be drastically simplified.
I do run actions for this project in nixos countainers and after checkout is done I make sure that clojure cli tool is available:
- run: nix-env -i clojure
To run depot in this example I just use example command from depot README.md
- name: run depot
run: |
clojure -Sdeps '{:aliases {:outdated {:replace-deps {olical/depot {:mvn/version "2.1.0"}}}}}' -M:outdated -m depot.outdated.main --every --write > deps-output.txt
After this is done we get two files that we care about:
deps.edn
- dependencies file that was modified by depotdeps-output.txt
- depot stdout that we can use as a body for a pull request
To transfer those two files we are going to utilize Github artifacts:
- uses: actions/upload-artifact@v2
with:
name: deps-output
path: deps-output.txt
- uses: actions/upload-artifact@v2
with:
name: deps
path: deps.edn
And now we can proceed with creating pull request.
It is important to tell our second job that it has to wait for the first one to complete.
Otherwise, there would be no files available for download from storage.
To do that we can simply say that job needs: bump
, bump
being the name of our first job of course.
First, lets download our two files from artifact storage:
- uses: actions/download-artifact@v2
with:
name: deps-output
- uses: actions/download-artifact@v2
with:
name: deps
Once this is done we need to read the output file and store it in output variable of a step.
To later reuse this variable we give this step id get-pr-body
.
- id: get-pr-body
run: |
body=$(cat deps-output.txt)
echo ::set-output name=body::$body
And now let’s create our pull request.
To allow this action to access our repository we will need to give it a token.
I used Personal Access Token with repo
scope fully enabled.
You can see how we extract body using variable inlining with ${{ steps.get-pr-body.outputs.body }}
.
- name: create PR
id: cpr
uses: peter-evans/create-pull-request@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
title: "[Automated] Update dependencies"
body: ${{ steps.get-pr-body.outputs.body }}
commit-message: "[Automated] Update dependencies"
branch: update-dependencies
labels: |
dependencies
Once this step succeeded we can print pull request information (mostly for debugging purposes)
- name: check outputs
run: |
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
And of course lets not forget to clean up after ourselves!
- uses: geekyeggo/delete-artifact@v1
with:
name: deps-output
- uses: geekyeggo/delete-artifact@v1
with:
name: deps
And that’s it! One day there might be a better tool or approach to solve this, but for now our small workflow should do just fine :)