I wrote a GitHub Action using Kotlin
Tags: actionshackathon, githubactions, kotlin, docker
Published: 2020-08-30 17:42:00

### My Workflow

I decided to take a look at GitHub Actions so for the past week I’ve been watching and reading everything about it. I even wrote a post, an [Introduction to GitHub Actions](https://le0nidas.gr/2020/08/29/introduction-to-github-actions/). What I found really interesting is the fact that you can write an action using the language you feel more comfortable with. And so I did!

I wrote a small action that:

1. collects all the changed files from a PR,
2. keeps those that have the .kt suffix,
3. runs ktlint on them and
4. makes a comment in the PR for every error that ktlint reports

and all that using Kotlin and some bash!

You can find it and use here: [ktlint-pr-comments](https://github.com/marketplace/actions/ktlint-pr-comments)

#### GitHub Action using Docker

There are three ways to create an action but only the one using Docker allows us to use the language we want.

In all three ways the main two ingredients are:

- the action’s code
- the action’s metadata, a file called `action.yml` which is placed in the root folder of your project and defines how the action will run and what inputs/outputs it has.

In our case there is also a third ingredient, a `Dockerfile` or a `docker image` which is used by the action’s runner to create a container and execute the action’s code inside it. All you have to do is to make sure that the action’s executable parts are being copied in the container and that are called upon its start.

The runner makes sure that the working space is being mounted to the container (in the state that it was just before the action is started) along with all the environment variables and the inputs the action needs. You can read more in the [documentation](https://docs.github.com/en/actions/creating-actions/creating-a-docker-container-action).

#### Ktlint PR comments

##### Action’s code

The action has three distinct parts.

The *first part* is responsible for using GitHub’s REST API to collect all of the PR’s changes and then keep those that are in Kotlin files and were added or modified. For that I used kscript and I was able to leverage all the libraries that I was accustomed to, like Retrofit and Moshi. When I was happy with the resulted script I used its `--package` option to create a standalone binary and copy it in the action’s Docker image.

The *second part* is a combination of bash commands that execute the ktlint binary by passing to it the results of the first part. Ktlint is being called with the `--reporter=json` parameter in order to create a JSON report.

The *third and final part* is again a kscript script that uses the report created before and GitHub’s REST API to make a PR line comment for every ktlint error that is part of the PR’s diff. Again a standalone binary was created and put in the image.

*Note:*

I like kscript since I can write things fast, easy and with all the libraries that I know but I also like writing test first and that proved to be quite difficult. So what I ended up doing was to act as if I was in a Kotlin project. I created my tests (using all my favorites like junit5, hamkrest and MockWebServer) and from that I created `.kt` files with the proper functionality. And for having a script I created a `.kts` file where I defined the external dependencies and included the `.kt` files:
{% gist https://gist.github.com/le0nidas/b6115592ef633cde3708f9ffd2e8249a %}

##### Action’s metadata

From the start what I wanted for this action was to be as autonomous as possible leaving very little responsibilities to the consumer and allowing her to just plug it in and watch it play.

For that the only input the action needs is a token, for allowing the kscript scripts to communicate with the API, which will most likely be the default `secrets.GITHUB_TOKEN` making the action’s usage as simple as adding the following lines in your workflow:

```yaml
- uses: le0nidas/ktlint-pr-comments@v1
  with:
    repotoken: ${{ secrets.GITHUB_TOKEN }}
```

##### Docker image

A lot of things must happen in order to have the action ready to run. Sdkman, Kotlin and kscript must be installed, the code needs be retrieved from the repository and both kscript scripts have be packaged. On top of that ktlint must be downloaded and placed in the proper path.

For all that, and to shave a few seconds from the action, I decided to have an image that has everything ready. So I created a [workflow](https://github.com/le0nidas/ktlint-pr-comments/blob/main/.github/workflows/build-and-push-docker-image.yml) that gets triggered every time there is a push in the main branch, builds and packs everything in an image and pushes the result to Docker Hub.

So now the action simply uses that image to run a container without any other ceremonies.

*Note:*

Before using Docker Hub I tried to use GitHub Packages but it turns out that public is not that public since it requires an authentication to retrieve a package.

#### Summary

That’s it! An action for having ktlint’s report as comments in your PR. A result of trial and error since I wrote it while learning about actions but I hope that someone could find it useful. If you do let me know!

An example of how to use it can be found in the action’s repository where I [dogfood it to the project](https://github.com/le0nidas/ktlint-pr-comments/blob/main/.github/workflows/check-pr.yml).

### Submission Category: 

Maintainer Must-Haves


### Yaml File or Link to Code

{% github https://github.com/le0nidas/ktlint-pr-comments %}