Skip to content

Commit

Permalink
build(mage): 🐛 split build and package environment generation
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuar committed Oct 3, 2024
1 parent da38244 commit 3b22ee4
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 51 deletions.
22 changes: 5 additions & 17 deletions build/magefiles/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import (
"errors"
"fmt"
"log/slog"
"os"
"path/filepath"
"runtime"

"github.com/magefile/mage/mg"
Expand Down Expand Up @@ -54,20 +52,13 @@ func (b Build) CI() error {

// buildProject is the shared method that all exported build targets use. It
// runs the bare minimum steps to build a binary of the agent.
//
//nolint:mnd
func buildProject() error {
// Remove any existing dist directory.
if err := os.RemoveAll(distPath); err != nil {
return fmt.Errorf("could not clean dist directory: %w", err)
}
// Recreate an empty dist directory for this build.
if err := os.Mkdir(distPath, 0o755); err != nil {
return fmt.Errorf("could not create dist directory: %w", err)
if err := cleanDir(distPath); err != nil {
return errors.Join(ErrBuildFailed, err)
}

// Set-up the build environment.
envMap, err := generateEnv()
buildEnv, err := generateBuildEnv()
if err != nil {
return errors.Join(ErrBuildFailed, err)
}
Expand All @@ -78,16 +69,13 @@ func buildProject() error {
return errors.Join(ErrBuildFailed, err)
}

// Set an appropriate output file based on the arch to build for.
outputFile := filepath.Join(distPath, "/go-hass-agent-"+envMap["PLATFORMPAIR"])

//nolint:sloglint
slog.Info("Running go build...",
slog.String("output", outputFile),
slog.String("output", buildEnv["OUTPUT"]),
slog.String("build.host", runtime.GOARCH))

// Run the build.
if err := sh.RunWithV(envMap, "go", "build", "-ldflags="+ldflags, "-o", outputFile); err != nil {
if err := sh.RunWithV(buildEnv, "go", "build", "-ldflags="+ldflags, "-o", buildEnv["OUTPUT"]); err != nil {
return fmt.Errorf("failed to build project: %w", err)
}

Expand Down
58 changes: 46 additions & 12 deletions build/magefiles/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"log/slog"
"os"
"path/filepath"
"runtime"
"slices"
"strings"
Expand Down Expand Up @@ -100,7 +101,7 @@ func getVersion() (string, error) {
return version, nil
}

// hash returns the git hash for the current repo or "" if none.
// getGitHash returns the git hash for the current repo or "" if none.
func getGitHash() (string, error) {
hash, err := sh.Output("git", "rev-parse", "--short", "HEAD")
if err != nil {
Expand All @@ -120,10 +121,10 @@ func getBuildDate() (string, error) {
return date, nil
}

// generateEnv will create a map[string]string containing environment variables
// and their values necessary for building the package on the given
// generateBuildEnv will create a map[string]string containing environment
// variables and their values necessary for building Go Hass Agent on the given
// architecture.
func generateEnv() (map[string]string, error) {
func generateBuildEnv() (map[string]string, error) {
envMap := make(map[string]string)

// CGO_ENABLED is required.
Expand All @@ -136,18 +137,12 @@ func generateEnv() (map[string]string, error) {
// Set APPVERSION to current version.
envMap["APPVERSION"] = version

// Set NFPM_ARCH so that nfpm knows how to package for this arch.
envMap["NFPM_ARCH"] = runtime.GOARCH

// Get the value of BUILDPLATFORM (if set) from the environment, which
// indicates cross-compilation has been requested.
_, arch, ver := parseBuildPlatform()

if arch != "" && arch != runtime.GOARCH {
slog.Info("Setting up cross-compilation.")
// Update NFPM_ARCH to the target arch.
envMap["NFPM_ARCH"] = arch + ver

// Set additional build-related variables based on the target arch.
switch arch {
case "arm":
Expand All @@ -169,14 +164,41 @@ func generateEnv() (map[string]string, error) {
envMap["PLATFORMPAIR"] = runtime.GOARCH
}

// Set an appropriate output file based on the arch to build for.
envMap["OUTPUT"] = filepath.Join(distPath, "/go-hass-agent-"+envMap["PLATFORMPAIR"])

return envMap, nil
}

// generatePkgEnv will create a map[string]string containing environment
// variables and their values necessary for packaging Go Hass Agent on the given
// architecture.
func generatePkgEnv() (map[string]string, error) {
envMap := make(map[string]string)

version, err := getVersion()
if err != nil {
return nil, fmt.Errorf("could not generate env: %w", err)
}
// Set APPVERSION to current version.
envMap["APPVERSION"] = version
// Set NFPM_ARCH so that nfpm knows how to package for this arch.
envMap["NFPM_ARCH"] = runtime.GOARCH
// Parse the build platform from the environment.
_, arch, ver := parseBuildPlatform()
// For arm, set the NFPM_ARCH to include the revision.
if arch != "" && arch != runtime.GOARCH {
slog.Info("Setting up cross-compilation.")
// Update NFPM_ARCH to the target arch.
envMap["NFPM_ARCH"] = arch + ver
}

return envMap, nil
}

// parseBuildPlatform reads the TARGETPLATFORM environment variable, which should
// always be set, and extracts the value into appropriate GOOS, GOARCH and GOARM
// (if applicable) variables.
//
//nolint:mnd
func parseBuildPlatform() (operatingsystem, architecture, version string) {
var buildPlatform string

Expand All @@ -199,3 +221,15 @@ func parseBuildPlatform() (operatingsystem, architecture, version string) {

return operatingsystem, architecture, version
}

func cleanDir(path string) error {
if err := os.RemoveAll(path); err != nil {
return fmt.Errorf("could not clean directory %s: %w", path, err)
}

if err := os.MkdirAll(path, 0o755); err != nil {
return fmt.Errorf("could not create directory %s: %w", path, err)
}

return nil
}
33 changes: 11 additions & 22 deletions build/magefiles/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@
package main

import (
"errors"
"fmt"
"log/slog"
"os"
"path/filepath"
"slices"
"strings"
Expand All @@ -21,49 +19,40 @@ import (
type Package mg.Namespace

var (
pkgformats = []string{"rpm", "deb", "archlinux"}
pkgPath = filepath.Join(distPath, "pkg")
nfpmBaseArgs = []string{"run", "github.com/goreleaser/nfpm/v2/cmd/nfpm", "package", "--config", ".nfpm.yaml", "--target", pkgPath}

ErrNoBuildEnv = errors.New("no build and/or version environment variables")
ErrNfpmInstallFailed = errors.New("unable to install nfpm")
pkgformats = []string{"rpm", "deb", "archlinux"}
pkgPath = filepath.Join(distPath, "pkg")
nfpmCommandLine = []string{"go", "run", "github.com/goreleaser/nfpm/v2/cmd/nfpm", "package", "--config", ".nfpm.yaml", "--target", pkgPath}
)

// Nfpm builds packages using nfpm.
//
//nolint:mnd,cyclop
func (Package) Nfpm() error {
if err := os.RemoveAll(pkgPath); err != nil {
return fmt.Errorf("could not clean dist directory: %w", err)
}

if err := os.MkdirAll(pkgPath, 0o755); err != nil {
return fmt.Errorf("could not create dist directory: %w", err)
if err := cleanDir(pkgPath); err != nil {
return fmt.Errorf("cannot run nfpm: %w", err)
}

envMap, err := generateEnv()
pkgEnv, err := generatePkgEnv()
if err != nil {
return fmt.Errorf("failed to create environment: %w", err)
return fmt.Errorf("cannot run nfpm: %w", err)
}

for _, pkgformat := range pkgformats {
slog.Info("Building package with nfpm.", slog.String("format", pkgformat))
args := slices.Concat(nfpmBaseArgs, []string{"--packager", pkgformat})
args := slices.Concat(nfpmCommandLine[1:], []string{"--packager", pkgformat})

if err := sh.RunWithV(envMap, "go", args...); err != nil {
if err := sh.RunWithV(pkgEnv, nfpmCommandLine[0], args...); err != nil {
return fmt.Errorf("could not run nfpm: %w", err)
}

// nfpm creates the same package name for armv6 and armv7 deb packages,
// so we need to rename them.
if envMap["GOARCH"] == "arm" && pkgformat == "deb" {
if pkgEnv["GOARCH"] == "arm" && pkgformat == "deb" {
debPkgs, err := filepath.Glob(distPath + "/pkg/*.deb")
if err != nil || debPkgs == nil {
return fmt.Errorf("could not find arm deb package: %w", err)
}

oldDebPkg := debPkgs[0]
newDebPkg := strings.ReplaceAll(oldDebPkg, "armhf", "arm"+envMap["GOARM"]+"hf")
newDebPkg := strings.ReplaceAll(oldDebPkg, "armhf", "arm"+pkgEnv["GOARM"]+"hf")

if err = sh.Copy(newDebPkg, oldDebPkg); err != nil {
return fmt.Errorf("could not rename old arm deb package: %w", err)
Expand Down

0 comments on commit 3b22ee4

Please sign in to comment.