go-githubactions gives Go authors inputs and log commands without a client object
Go SDK for GitHub Actions - easily author GitHub Actions in Go
At a glance
- What is it?
- go-githubactions is an Apache-2.0 Go SDK for writing GitHub Actions, with no external dependencies, a flat package root API for reading inputs and writing workflow commands, and an instance form that attaches fields such as a file and a line to every log line. It suits teams that want their Actions in Go, and its size story depends on the multi-stage container build the readme walks through.
- Who is it for?
- Teams writing their automation in Go, or maintaining a shared set of internal Actions across many repositories, are the people this library is for, and the no-dependency property is what makes it safe to drop into a CI pipeline where every addition is something to audit.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 97 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
An SDK for writing GitHub Actions in Go, with no dependencies
The library's job is to make Go a first-class language for GitHub Actions. The readme describes it as an SDK for authoring GitHub Actions in Go, with no external dependencies and a Go-like interface for interacting with the Actions build system. Those two properties are what make it worth a look. Most people who write Actions in Go end up reading environment variables by hand and shelling out to set outputs, and a library that gives you a GetInput function and a set of logging helpers removes that boilerplate. The no-dependencies claim matters for a different reason: this code runs inside a CI runner on every commit, and every dependency is another thing to audit and another thing that can break a build for reasons that have nothing to do with your code. The module path is github.com/sethvargo/go-githubactions, the licence is Apache-2.0, and the newest release is v1.4.0 from 2026-07-17.
Installing is one go get, and the entry point is the package root
Getting the library is a single command, and the readme notes it downloads the library with the upgrade flag set.
$ go get -u github.com/sethvargo/go-githubactions/...The usage model is deliberately flat. The easiest way to use the library is to import it and invoke the functions at the root, so there is no client object to construct and no configuration to pass for the common case.
import (
"github.com/sethvargo/go-githubactions"
)
func main() {
val := githubactions.GetInput("val")
if val == "" {
githubactions.Fatalf("missing 'val'")
}
}The shape of that example is the shape of most Actions code. You read an input by name, you check whether it is empty, and if it is you stop the step with a message. The Fatalf helper is the reason the library earns its place even in a three-line action, because writing to the Actions error log format and exiting non-zero is otherwise a detail every action has to get right.
WithFieldsMap gives each log line a context
The second usage pattern adds fields to log messages, and it solves a problem that becomes obvious as soon as an action handles more than one thing. Instead of the root functions, you create an instance carrying a map of extra fields, and every message logged through that instance includes them.
import (
"github.com/sethvargo/go-githubactions"
)
func main() {
actions := githubactions.WithFieldsMap(map[string]string{
"file": "myfile.js",
"line": "100",
})
val := actions.GetInput("val")
if val == "" {
actions.Fatalf("missing 'val'")
}
}The fields are emitted as workflow command syntax, which is the mechanism Actions itself uses for annotations, so a file and a line in the fields turn a plain log line into something the GitHub interface can attach to the right place in the diff. That is the practical difference between the root call and the instance, and it is why the API is shaped around an instance rather than around a global that you set fields on.
Publishing a Go action means choosing one of three routes
The readme lists three ways to publish a Go action, and the choice between them is the most consequential decision in the project. Composite actions are listed first and linked to an example. The second is pre-compiled binaries with a shim, linked to a write-up on how one team does it. The third is Docker containers, which is the route the readme then works through in detail because it has the most moving parts. The background is that by default GitHub Actions expects actions to be written in Node.js, so for other languages such as Go you need to provide a Dockerfile and entrypoint instructions in an action.yml file.
# your-repo/Dockerfile
FROM golang:1.25
WORKDIR /src
COPY . .
RUN go build -o /bin/app .
ENTRYPOINT ["/bin/app"]And the matching action definition names the image and says the runtime is Docker.
# your-repo/action.yml
name: My action
author: My name
description: My description
runs:
using: docker
image: DockerfileThe two-file arrangement is the cost of this route. Users then import the action by repository name in their workflow.
Cloning and compiling on every run is the problem this solves
The readme is blunt about why the straightforward Dockerfile route is not good enough. Using the action by repository name will clone the entire repo and compile the Go code each time the action runs, and worse, it uses the Go base container which is a few hundred megabytes and includes a ton of unnecessary things. On a busy repository that is paid on every commit, and on a slow runner it is the difference between a workflow that takes a minute and one that takes several. The fix is to stop cloning the repository at run time and instead publish a prebuilt container that GitHub pulls directly, which the readme shows with the docker scheme.
steps:
- name: My action
uses: docker://username/repo:latestSo the container is built once and pushed, and the runner fetches the image the same way it would fetch any other container. The remaining problem is that the image is still too large, and that is where the multi-stage build comes in.
A multi-stage build gets the image under ten megabytes
The full Dockerfile in the readme is the piece worth copying, because it solves size, static linking and running as a non-root user in one file.
FROM golang:1.25 AS builder
ENV CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64
RUN apt-get -qq update && \
apt-get -yqq install upx
WORKDIR /src
COPY . .
RUN go build \
-ldflags "-s -w -extldflags '-static'" \
-o /bin/app \
. \
&& strip /bin/app \
&& upx -q -9 /bin/app
RUN echo "nobody:x:65534:65534:Nobody:/:" > /etc_passwd
FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /etc_passwd /etc/passwd
COPY --from=builder --chown=65534:0 /bin/app /app
USER nobody
ENTRYPOINT ["/app"]The readme explains the two stages in turn. The first uses a fat container to build, strip and compress the compiled Go binary, and the second copies the compiled and compressed binary into a scratch container along with some SSL certificates and a nobody user in which to execute the container, with the passwd entry written into a file in the builder stage and copied across. The result is usually an image of less than ten megabytes, which makes for much faster builds. Three details in there are worth knowing individually. CGO is disabled and the binary is linked statically, which is what allows it to run on a scratch image that has no C library. The upx step compresses the executable, and the strip and the linker flags shrink it further. And the SSL certificates are copied in explicitly, because a scratch image has none, and an action that makes HTTPS requests will fail without them.
The repository is a small, well-tested Go package
The tree at the top level shows a library rather than a framework. The implementation is a handful of files with a name for each concern: actions.go, actions_root.go for the root package functions, command.go for the workflow command formatting, options.go, and two small files named eof_crlf.go and eof_lf.go that handle the two line ending conventions GitHub accepts in logs. The test files sit next to their subjects with the _test suffix, including a doc_test file, and there is a .github directory holding the unit test workflow that the badge in the readme points at. The go.mod file is minimal, with the module path, the Go 1.25 directive and the Apache licence header, and there is no require block at all, which is the module-level statement of the no-external-dependencies claim. The Makefile has a test target that runs with the count flag set to one, the short flag, shuffle on and a five minute timeout, and a separate acceptance target that adds the race detector, which is the standard Go pattern for separating fast unit runs from slower instrumented ones.
Where go-githubactions is the wrong choice
The first wrong case is a team that has standardised on JavaScript actions, because the JavaScript runtime is what Actions expects by default and the composite action route listed first in the readme needs no container at all. The second is an action that does one trivial thing, since for a step that reads an input and prints it, the whole SDK plus a Docker image is heavier than the step. The third is a project that cannot publish a container, where the pre-compiled binary with a shim route is the one to look at instead. The fourth is a team that has already standardised on a different Actions helper in Go, since the ecosystem has more than one of these and mixing them in a single workflow is a maintenance cost. The fifth is anyone who needs a capability the SDK does not expose, because the library's stated scope is interacting with the build system through inputs and log output, and a requirement such as caching artefacts or managing a matrix directly is a case for the Actions documentation rather than for this package. The last is a workflow that already builds a container image, because in that case the multi-stage Dockerfile is unnecessary and you can point the action at the image you were going to publish anyway.
Editorial conclusion
Teams writing their automation in Go, or maintaining a shared set of internal Actions across many repositories, are the people this library is for, and the no-dependency property is what makes it safe to drop into a CI pipeline where every addition is something to audit. It is the wrong choice for a single trivial step, and it is the wrong choice for a team standardising on JavaScript Actions, since the JavaScript runtime is what Actions expects by default and needs no container. Check three things before you adopt it: that your runners can pull a container image at all, since the smallest and fastest route depends on publishing one, that the container build disables cgo and links statically, because the readme's scratch image has no C library and that is what makes it work, and that you copy the SSL certificates into the final image, since a scratch image has none and an action making HTTPS requests will fail without them. The module path is github.com/sethvargo/go-githubactions, the licence is Apache-2.0, the newest release is v1.4.0 from 2026-07-17, and the repository publishes no GitHub releases.
Frequently asked questions
What does go-githubactions provide?
It is an SDK for authoring GitHub Actions in Go, with no external dependencies and a Go-like interface for interacting with the Actions build system. The entry point is the package root, so you import it and call the functions directly rather than constructing a client.
How do I install the GitHub Actions SDK for Go?
With go get -u github.com/sethvargo/go-githubactions/... . The module path is github.com/sethvargo/go-githubactions, the go.mod requires Go 1.25 and declares no dependencies at all, and the licence is Apache-2.0.
What does WithFieldsMap do in go-githubactions?
It creates an instance that carries extra fields to be included in log messages, for example a file and a line. You then call GetInput and the logging helpers on that instance instead of on the package root, so every message it logs carries the same context.
How do I publish a Go GitHub Action?
The readme lists three routes: composite actions, pre-compiled binaries with a shim, and Docker containers. For the container route you supply a Dockerfile and an action.yml with runs using docker, and users then reference the action by repository name, for example uses: username/repo@latest.
Why does the Docker image for a Go action need multiple build stages?
Because using the action by repository name clones the whole repo and compiles the Go code on every run, using the Go base container which is hundreds of megabytes. The multi-stage build compiles, strips and compresses the binary in a fat builder stage, then copies it into a scratch container with the SSL certificates and a nobody user, which usually produces an image of less than ten megabytes.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sethvargo-go-githubactions)Was this useful?