26 Jun 2022

godocker

Custom GitHub Action with Go

Author

Gurleen Sethi

Custom GitHub Action with Go

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/[email protected]
      - uses: gurleensethi/github-action-[email protected] # 👈 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/[email protected]
      - 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.

Email Newsletter Icon
Don't miss new articles
Get notified once/twice per month when new articles are published.

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/[email protected]
      - uses: gurleensethi/github-action-[email protected]
      	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 🙏🏻.

Table of Contents
TheDeveloperCafe