Skip to content

Pre‐built static binaries

Ryan Parman edited this page Apr 2, 2024 · 8 revisions

Setup

Generally, for things that are static binaries (already compiled), we'll just use the latest Ubuntu Docker image.

We use GoReleaser Pro to configure how the package is built. This allows us to define some shared configuration, so that we only need to implement the things that are specific to this package.

This will allow us to build .apk, .deb, and .rpm packages for both linux/amd64 and linux/arm64 builds.

Downloading from GitHub

For packages that already publish builds as GitHub releases, we use download-asset to download the asset, extract it, and pull out the binary we care about.

To do this, we define a new download.sh file. This file calls download-asset to perform the downloads. The download.sh file is executed by GoReleaser as a pre-build step.

Creating package definition

You'll need a few pieces of information.

  • The name of the binary.
  • The owner/repository of the GitHub repository.
  • The URL for the project (might be the GitHub URL).
  • The software license for the project.
  • The description of the project (often in the upper-right of the project page).
  • The pattern for the release asset.
  • Determine an appropriate Cron expression for scheduled updates. See Cron expressions for more information.

From the root of the repository, you can run:

go run generate-workflow.go \
    -t _download-and-package.gotmpl.yml \
    -p {package} \
    -r '{owner/repository}' \
    -c '{cron-expression}' \
    ;

This will generate the workflow for Pre‐built static binaries. (Other types of builds will probably have different workflow templates.) Needing to make changes is improbable.

Packaging

Take an existing directory inside the packages/ directory (tenv is a good example), duplicate the directory, and rename it to the name of the package from the previous step.

Ideally, I've been able to abstract-away anything that isn't directly relevant to this one package. There are two files you'll need to edit:

.goreleaser.yml

.goreleaser.yml contains the information that is related to packaging metadata: the name of the package, its URL, license, description, etc. You identified these in the earlier step; you just need to fill them in.

One binary per CPU architecture (2 binaries)

If there is one binary for all flavors of the Linux C standard library (usually Go), then your .goreleaser.yml file should reference:

  • _builds.yml
  • _nfpms.yml

This will create an .rpm, .deb, and .apk package for x86_64/amd64, then a second set of packages for aarch64/arm64.

Two binaries per CPU architecture (4 binaries)

If there are separate binaries for different flavors of the Linux C standard library (usually Rust), then your .goreleaser.yml file should reference:

  • _builds_glibc_musl.yml
  • _nfpms_glibc_musl.yml

This will create an .rpm and .deb package for glibc on x86_64/amd64, then a second set of packages for glibc on aarch64/arm64. Then it will create an .apk for musl on x86_64/amd64 and musl on aarch64/arm64.

download.sh

This leverages download-asset (which is preinstalled) to perform the downloads and extractions. Here, you'll need to know:

  • the owner/repository
  • the pattern of the GitHub release asset (one for Intel64, one for ARM64)
  • the name of the binary inside of the tarball (or blank if the asset itself is a binary).
    • Usually, this is just the name of the binary. Not always, though.
  • and the name to give the binary when it is extracted and saved to the file system

One binary per CPU architecture (2 binaries)

If there is one binary for all flavors of the Linux C standard library (usually Go), then your download.sh file should save the binaries to:

  • {package}_amd64
  • {package}_arm64

Two binaries per CPU architecture (4 binaries)

If there are separate binaries for different flavors of the Linux C standard library (usually Rust), then your .goreleaser.yml file should reference:

  • {package}_glibc_amd64
  • {package}_glibc_arm64
  • {package}_musl_amd64
  • {package}_musl_arm64

Building for glibc OR musl

In some cases, there are reasons to only build one or the other. For example, Alpine Linux's repositories tend to be more progressive and include modern packages than Ubuntu/Debian or Red Hat/CentOS. For this reason, it might make sense to skip packaging.

For packages with separate binaries for glibc and musl, you can use:

  • _builds_glibc.yml
  • _nfpms_glibc.yml

…or…

  • _builds_musl.yml
  • _nfpms_musl.yml

…to build packages for one or the other, but not both.

Uploading to repository

This automatically handled by the workflow.