Build and package a (reproducible) Go binary.
- Define the build.
- Assert that it is reproducible (optionally).
- Use the resultant artifacts in your workflow.
This is intended for internal HashiCorp use only; Internal folks please refer to RFC ENGSRV-084 for more details.
- Results are zipped using standard HashiCorp naming conventions.
- You can include additional files in the zip like licenses etc.
- Convention over configuration means minimal config required.
- Reproducibility is checked at build time.
- Fast feedback if accidental nondeterminism is introduced.
The core functionality of this action is contained in a Go CLI, which you can also install and use locally. See the CLI docs for more.
This Action can run on both Ubuntu and macOS runners.
This example shows building a single linux/amd64 binary.
See this simple example workflow running here.
name: Minimal Example (main)
on: push
jobs:
example:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build
uses: hashicorp/actions-go-build@main
with:
go_version: 1.24
os: linux
arch: amd64
work_dir: testdata/example-app
debug: true
instructions: |
go build -o "$BIN_PATH" -trimpath -buildvcs=falseThis example shows usage of the action inside a matrix configured to produce
binaries for different platforms. It also injects the version, revision, and
revision time into the binary via -ldflags, uses the netcgo tag for darwin,
and disables CGO for linux and windows builds.
See this matrix example workflow running here.
name: Matrix Example (main)
on: push
jobs:
example:
runs-on: ${{ matrix.runner }}
strategy:
matrix:
include:
- { runner: macos-latest, os: darwin, arch: amd64, tags: netcgo }
- { runner: macos-latest, os: darwin, arch: arm64, tags: netcgo }
- { runner: ubuntu-latest, os: linux, arch: amd64, env: CGO_ENABLED=0 }
- { runner: ubuntu-latest, os: linux, arch: amd64, env: CGO_ENABLED=0 }
- { runner: ubuntu-latest, os: windows, arch: amd64, env: CGO_ENABLED=0 }
steps:
- uses: actions/checkout@v3
- name: Build
uses: hashicorp/actions-go-build@main
with:
product_name: example-app
product_version: 1.2.3
go_version: 1.24
os: ${{ matrix.os }}
arch: ${{ matrix.arch }}
instructions: |-
cd ./testdata/example-app && \
${{ matrix.env }} \
go build \
-o "$BIN_PATH" \
-trimpath \
-buildvcs=false \
-tags="${{ matrix.tags }}" \
-ldflags "
-X 'main.Version=$PRODUCT_VERSION'
-X 'main.Revision=$PRODUCT_REVISION'
-X 'main.RevisionTime=$PRODUCT_REVISION_TIME'
"| Name | Description |
|---|---|
product_name (optional) |
Used to calculate default bin_name and zip_name. Defaults to repository name. |
product_version (optional) |
Full version of the product being built (including metadata). |
product_version_meta (optional) |
The metadata field of the version. |
go_version (required) |
Version of Go to use for this build. |
os (required) |
Target product operating system. |
arch (required) |
Target product architecture. |
reproducible (optional) |
Assert that this build is reproducible. Options are assert (the default), report, or nope. |
bin_name (optional) |
Name of the product binary generated. Defaults to product_name minus any -enterprise suffix. |
zip_name (optional) |
Name of the product zip file. Defaults to <product_name>_<product_version>_<os>_<arch>.zip. |
work_dir (optional) |
The working directory, to run the instructions in. Defaults to the current directory. |
instructions (required) |
Build instructions to generate the binary. See Build Instructions for more info. |
debug (optional) |
Enable debug-level logging. |
clean (optional) |
Build with the clean flag on. |
| Name | Description |
|---|---|
zip_name |
The provided or calculated zip file name |
target_dir |
Name of the directory where an artifact can be assembled |
The instructions input is a bash script that builds the product binary.
It should be kept as simple as possible.
Typically this will be a simple go build invocation,
but it could hit a make target, or call another script.
See Example Build Instructions
below for examples of valid instructions.
The instructions must use the environment variable $BIN_PATH
because the minimal thing they can do is to write the compiled binary to $BIN_PATH.
In order to add other files like licenses etc to the zip file, you need to
write them into $TARGET_DIR in your build instructions.
When the instructions are executed, there are a set of environment variables
already exported that you can make use of
(see Environment Variables below).
| Name | Description |
|---|---|
PRODUCT_NAME |
Same as the product_name input. |
PRODUCT_VERSION |
Same as the product_version input. |
PRODUCT_REVISION |
The git commit SHA of the product repo being built. |
PRODUCT_REVISION_TIME |
UTC timestamp of the PRODUCT_REVISION commit in iso-8601 format. |
OS |
Same as the os input. |
ARCH |
Same as the arch input. |
GOOS |
Same as OS. |
GOARCH |
Same as ARCH. |
WORKTREE_DIRTY |
Whether the workrtree is dirty (true or false). |
WORKTREE_HASH |
Unique hash of the work tree. Same as PRODUCT_REVISION unless WORKTREE_DIRTY. |
TARGET_DIR |
Absolute path to the zip contents directory. |
BIN_PATH |
Absolute path to where instructions must write Go executable. |
The reproducible input has three options:
assert(the default) means perform a verification build and fail if it's not identical to the primary build.reportmeans perform a verification build, log the results, but do not fail.nopemeans do not perform a verification build at all.
See Ensuring Reproducibility, below for tips on making your build reproducible.
The examples below all illustrate valid build instructions using go build flags
that give the build some chance at being reproducible.
Simplest Go 1.17 invocation. (Uses -trimpath to aid with reproducibility.)
instructions: go build -o "$BIN_PATH" -trimpathSimplest Go 1.18+ invocation. (Additionally uses -buildvcs=false to aid with reproducibility.)
instructions: go build -o "$BIN_PATH" -trimpath -buildvcs=falseMore complex build, including copying a license file into the zip and cding into
a subdirectory to perform the go build.
instructions: |
cp LICENSE "$TARGET_DIR/"
cd sub/directory
go build -o "$BIN_PATH" -trimpath -buildvcs=falseAn example using make:
instructions: make buildWith this Makefile:
build:
go build -o "$BIN_PATH" -trimpath -buildvcs=falseSee also the example workflow above,
which injects info into the binary using -ldflags.
If you are aiming to create a reproducible build, you need to at a minimum ensure that your build is independent from the time it is run, and from the path that the module is at on the filesystem.
Embedding the actual 'build time' into your binary will ensure that it isn't reproducible,
because this time will be different for each build. Instead, you can use the
PRODUCT_REVISION_TIME which is the time of the latest commit, which will be the same
for each build of that commit.
By default go build embeds the absolute path to the source files inside the binaries
for use in stack traces and debugging. However, this reduces reproducibility because
that path is likely to be different for different builds.
Use the -trimpath flag to remove the portion of the path that is dependent on the
absolute module path to aid with reproducibility.
Go 1.18+ embeds information about the current checkout directory of your code, including
modified and new files. In some cases this interferes with reproducibility. You can
turn this off using the -buildvcs=false flag.
Development docs have moved to docs/development.md.
The core functionality of this action is contained in a Go CLI, which can also be installed and run locally. See the CLI docs for instructions on installing and using the CLI.