Categorygithub.com/majohn-r/tools-build
modulepackage
0.13.0
Repository: https://github.com/majohn-r/tools-build.git
Documentation: pkg.go.dev

# README

tools-build

GoDoc Reference go.mod LICENSE

Release Code Coverage Report Go Report Card Build Status

This package provides build script tooling for Go-based projects. The tooling is in the form of code utilizing goyek build automation. goyek builds are typically set up as tasks, and this project provides some common code to perform the work of the tasks. Here is a sample set of tasks:

var (
    build = goyek.Define(goyek.Task{
        Name:  "build",
        Usage: "build the executable",
        Action: func(a *goyek.A) {
            buildExecutable(a)
        },
    })

    clean = goyek.Define(goyek.Task{
        Name:  "clean",
        Usage: "delete build products",
        Action: func(a *goyek.A) {
            fmt.Println("deleting build products")
            exec, path, _, _ := readConfig()
            workingDir := WorkingDir()
            files := []string{
                filepath.Join(workingDir, path, versionInfoFile),
                filepath.Join(workingDir, path, resourceFile),
                filepath.Join(workingDir, coverageFile),
                filepath.Join(workingDir, exec),
            }
            Clean(files)
        },
    })

    _ = goyek.Define(goyek.Task{
        Name:  "coverage",
        Usage: "run unit tests and produce a coverage report",
        Action: func(a *goyek.A) {
            GenerateCoverageReport(a, coverageFile)
        },
    })

    _ = goyek.Define(goyek.Task{
        Name:  "doc",
        Usage: "generate documentation",
        Action: func(a *goyek.A) {
            GenerateDocumentation(a)
        },
    })

    format = goyek.Define(goyek.Task{
        Name:  "format",
        Usage: "clean up source code formatting",
        Action: func(a *goyek.A) {
            Format(a)
        },
    })

    lint = goyek.Define(goyek.Task{
        Name:  "lint",
        Usage: "run the linter on source code",
        Action: func(a *goyek.A) {
            Lint(a)
        },
    })

    nilaway = goyek.Define(goyek.Task{
        Name:  "nilaway",
        Usage: "run nilaway on source code",
        Action: func(a *goyek.A) {
            NilAway(a)
        },
    })

    vulnCheck = goyek.Define(goyek.Task{
        Name:  "vulnCheck",
        Usage: "run vulnerability check on source code",
        Action: func(a *goyek.A) {
            VulnerabilityCheck(a)
        },
    })

    _ = goyek.Define(goyek.Task{
        Name:  "preCommit",
        Usage: "run all pre-commit tasks",
        Deps:  goyek.Deps{clean, lint, nilaway, format, vulnCheck, tests, build},
    })

    tests = goyek.Define(goyek.Task{
        Name:  "tests",
        Usage: "run unit tests",
        Action: func(a *goyek.A) {
            UnitTests(a)
        },
    })
)

And here is a typical build script to execute the tasks:

#!/bin/bash

set -o errexit
set -o nounset
set -o pipefail

if [[ "${TRACE-0}" == "1" ]]; then
    set -o xtrace
    tracing=true
else
    tracing=false
fi

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
cd "${DIR}/build"
if [[ "${tracing}" == "true" ]]; then
    DIR=${DIR} go run . -v "$@"
else
    DIR=${DIR} go run . "$@"
fi

The script employs a DIR environment variable for the benefit of the WorkingDir function, which is used by this package to find the project's top level directory. If DIR is not set as an environment variable, WorkingDir will assume that ".." is the correct location, which is based on the assumption that the go code running the build is placed in a directory, one level deep, such as build (as seen in the line above cd "${DIR}/build). Regardless of whether the DIR environment variable is set, the WorkingDir function looks for the .git directory in its candidate value, and it's not found, then the WorkingDir function calls os.Exit and the build ends.

Opinionated?

Well, yes. I wrote this for my go projects, and, as such, it reflects my thinking about the proper tooling to use, and how to use that tooling. The biggest example of this is probably my use of gocritic as the tool called by the Lint function.

That said, if you find the package useful but don't like some of my choices, you can easily create your own functions to replace the ones you don't care for. Won't hurt my feelings a bit.

# Functions

AllDirs returns all directories in the directory specified by the top parameter, including that directory.
Clean deletes the named files, which must be located in, or in a subdirectory of, WorkingDir().
Deadcode runs dead code analysis on the source code after making sure that the deadcode tool is up-to-date; returns false on failure.
EatTrailingEOL removes trailing \n and \r characters from the end of a string.
Format runs the gofmt tool to repair the formatting of each source file; returns false if the command fails.
FormatSelective runs the gofmt tool to repair the formatting of selected source files; returns false if the command fails.
Generate runs the 'go generate' tool.
GenerateCoverageReport runs the unit tests, generating a coverage profile; if the unit tests all succeed, generates the report as HTML to be displayed in the current browser window.
GenerateDocumentation generates documentation of the code, outputting it to stdout; returns false on error.
IncludesRelevantFiles returns true if the provided directory contains any regular files whose names conform to the fileMatcher.
Install runs the command to install the '@latest' version of a specified package; returns false on failure.
IsMalformedFileName determines whether a file name is malformed, such that it could be used to access a file outside the working directory (starts with '/' or '\\', or contains a path component of '..'.
IsRelevantFile returns true if the entry is a file and its name is validated by the provided fileMatcher.
Lint runs lint on the source code after making sure that the lint tool is up-to-date; returns false on failure.
MatchGoSource matches a file name ending in '.go', but does not match test files.
NilAway runs the nilaway tool, which attempts, via static analysis, to detect potential nil access errors; returns false on errors.
PrintBuffer sends the buffer contents to stdout, but first strips trailing EOL characters, and then only prints the remaining content if that content is not empty.
RelevantDirs returns the directories that contain files matching the provided fileMatcher.
RestoreEnvVars reverts the environment changes made by SetupEnvVars.
RunCommand runs a command and displays all of its output; returns true on success.
SetupEnvVars executes the intent of the provided slice of EnvVarMementos, and returns a slice to be executed to revert the directed changes.
TaskDisabled returns true if the provided taskName matches (case-insensitively) one of the comma-delimited values in the disable flag's value.
UnacceptableWorkingDir determines whether a specified candidate directory could be the working directory for the build.
UnitTests runs all unit tests, with code coverage enabled; returns false on failure.
UpdateDependencies updates module dependencies and prunes the modified go.mod and go.sum files.
VulnerabilityCheck runs the govulncheck tool, which checks for unresolved known vulnerabilities in the libraries used; returns false on failure.
WorkingDir returns a 'best' guess of the working directory.

# Variables

AggressiveFlag is a flag for the UpdateDependencies function to more aggressively get updates.
BuildFS is the file system used; accessible so that tests can override it.
CachedWorkingDir is the cached working directory.
ExecFn is the goyek Exec function.
ExitFn is the os.Exit function, set as a variable so that unit tests can override.
NoFormatFlag is a flag to disable formatting from the deadcode command.
NoTestFlag is a flag to remove the -test parameter from the deadcode command.
PrintlnFn is the fmt.Println function, set as a variable so that unit tests can override.
SetenvFn is the os.Setenv function, set as a variable so that unit tests can override.
TemplateFlag is a flag that allows the caller to change the format template used by the deadcode command.
UnsetenvFn is the os.Unsetenv function, set as a variable so that unit tests can override.

# Structs

EnvVarMemento captures an environment variable's desired state.