Custom GitHub Action with Go

Read more articles on Go, Docker
github logo
Github Repository
Code for this articles is available on GitHub
Custom GitHub Action with Go

Subscribe

Get updated 1 - 2 times a month when new articles are published, no spam ever.

Getting Started #

In this article you are going to learn how to create a custom GitHub Action in Go. GitHub provides us two ways to build custom GitHub Actions, you can either build actions using JavaScript šŸ’© or you can use Docker šŸ³. I am going to use Docker + Go to build custom GitHub Action, while doing so I will point out some important things to take note of.

This articles assumes the following pre-requisites:

  • You know what GitHub Actions are and you have used them before.
  • You have basic understanding of Docker.

Writing Go Code #

For now lets keep the code simple and just print a message. Create a main.go file and add the following code to it.

package main

import "fmt"

func main() {
	fmt.Println("Hello World From GitHub Action")
}
main.go

Writing Dockerfile #

There is no need to do anything specific for the Dockerfile, all you need is a regular docker image that runs the go code. Create a new Dockerfile and add the following code to it.

FROM golang:1.18.3-alpine3.16

WORKDIR /app

COPY ./ ./

RUN go build -o /bin/app main.go

ENTRYPOINT ["app"]
Dockerfile

You might have noticed that (RUN go build -o /bin/app main.go) when building the go code the executable is being stored inside the bin directory. This is not a requirement, you can store the executable wherever you want.

Writing action.yml #

action.yml is the file that tells GitHub "Hey GitHub! This repository contains a GitHub Action". You can define a lot of metadata related to the action in this file. I highly recommend to give this documentation a read if you are developing a GitHub Action šŸ‘‰ Metadata syntax for GitHub Actions.

Create a new action.yml file and add the following code to it.

name: "github-action-go"
description: "An example of building github actions with Go"
runs:
  using: docker
  image: Dockerfile
action.yml

The runs section is the one where you can specify to GitHub to run this action using Docker.

Publishing the Action #

Now you are all set to go, create a new repository and push the code. Before you can use an action you need to publish it by creating a release on GitHub. On the repository click on create a new release.

GitHub create new release

You will need to create a tag before the release, create a tag named v1 (you can name it whatever you want but keeping proper versions is highly recommended).

GitHub action create tag

Give the release a title and click on Publish Release.

Using the Action #

Once the release is created you can start using the action in any github repositories, an example workflow file would look something like this:

jobs:
  github-action-go:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: gurleensethi/github-action-go@v1 # šŸ‘ˆ using custom action
.github/workflows/workflow.yml

You can also use your custom action in the same repository as the action itself.

jobs:
  github-action-go:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: ./ # šŸ‘ˆ using action from the repository itself.
.github/workflows/workflow.yml

How GitHub runs Docker actions #

Since the action is supposed to run in as a docker container GitHub uses the docker run command to run it, it provides a bunch of arguments. Brave yourself to see the giant command.

docker run --name e225889822a53557c5068f1d4400b154c_a1b5de --label 72882e 
--workdir /github/workspace --rm -e HOME -e GITHUB_JOB -e GITHUB_REF -e GITHUB_SHA 
-e GITHUB_REPOSITORY -e GITHUB_REPOSITORY_OWNER -e GITHUB_RUN_ID 
-e GITHUB_RUN_NUMBER -e GITHUB_RETENTION_DAYS -e GITHUB_RUN_ATTEMPT 
-e GITHUB_ACTOR -e GITHUB_WORKFLOW -e GITHUB_HEAD_REF -e GITHUB_BASE_REF 
-e GITHUB_EVENT_NAME -e GITHUB_SERVER_URL -e GITHUB_API_URL -e GITHUB_GRAPHQL_URL 
-e GITHUB_REF_NAME -e GITHUB_REF_PROTECTED -e GITHUB_REF_TYPE -e GITHUB_WORKSPACE 
-e GITHUB_ACTION -e GITHUB_EVENT_PATH -e GITHUB_ACTION_REPOSITORY 
-e GITHUB_ACTION_REF -e GITHUB_PATH -e GITHUB_ENV -e GITHUB_STEP_SUMMARY 
-e RUNNER_OS -e RUNNER_ARCH -e RUNNER_NAME -e RUNNER_TOOL_CACHE -e RUNNER_TEMP 
-e RUNNER_WORKSPACE -e ACTIONS_RUNTIME_URL -e ACTIONS_RUNTIME_TOKEN 
-e ACTIONS_CACHE_URL -e GITHUB_ACTIONS=true -e CI=true 
-v "/var/run/docker.sock":"/var/run/docker.sock" 
-v "/home/runner/work/_temp/_github_home":"/github/home" 
-v "/home/runner/work/_temp/_github_workflow":"/github/workflow" 
-v "/home/runner/work/_temp/_runner_file_commands":"/github/file_commands" 
-v "/home/runner/work/github-action-go/github-action-go":"/github/workspace" 72882e:225889822a53557c5068f1d4400b154c

There is bunch of good stuff here, it sets the working directory --workdir /github/workspace, passes a bunch of environment variables using the -e flag and attaches a lot of volumes. Give this command a good read it might help you in something you are developing.

Action Input and Container Arguments #

GitHub Actions allow you to specify inputs which the user of the action can pass when defining the workflow file.

The code for this section is available in the input_args branch of the repository.

Let's refactor the code to accept the message from the user instead of harcoding it. Update the main.go code to print all the arguments passed to the program.

package main

import (
	"fmt"
	"strings"
	"os"
)

func main() {
	fmt.Println(strings.Join(os.Args[1:], " "))
}
main.go

If you run go run main.go hello world you will get the output hello world.

Update the action.yml to accept input from the user, let's call it message.

name: "github-action-go"
description: "An example of building github actions with Go"

inputs:
  message:
    required: true

runs:
  using: docker
  image: Dockerfile
action.yml

As you can see I have added a new inputs section where I have defined an input called message.

This input needs to be passed as args to the go program, you can do this by using the args option under run. Any args you specify with args will be passed to the docker run command when the action is run.

name: "github-action-go"
description: "An example of building github actions with Go"

inputs:
  message:
    required: true
    
runs:
  using: docker
  image: Dockerfile
  args:
    - ${{ inputs.message }}
action.yml

Update the worflow file to use this message input.

jobs:
  github-action-go:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: gurleensethi/github-action-go@v1
      	with:
        	message: Hello World from Github Action Input
.github/workflows/workflow.yml

Running the workflow you can see the specified message šŸŽ‰ and you can also see that the docker run command is being passed the arguments.

GitHub docker input args

There is so much more you can do with GitHub Actions and even more with the beautiful language of Go, go out there and build some actions šŸ˜‰.

Thank you for reading this article šŸ™šŸ».

Subscribe

Get updated 1 - 2 times a month when new articles are published, no spam ever.

    TheDeveloperCafe Ā© 2022-2024