0
0
mirror of https://github.com/thegeeklab/wp-docker-buildx.git synced 2024-11-24 02:50:38 +00:00
wp-docker-buildx/docker/docker.go
Robert Kaussow a2bc0869af
fix: use StringMap for build_args instead of StringSlice (#277)
BREAKING CHANGE: The type of the `build_args` property was changed from `list` to `map` to support the use of secrets for certain build arguments. During this refactoring, the `build_args_from_env` behavior was also changed to achieve the intended behavior. Environment variable names passed to this list will be used as build arguments if the environment variable is set.
2024-11-15 21:45:29 +01:00

250 lines
6.0 KiB
Go

package docker
import (
"fmt"
"maps"
"os"
"strings"
"time"
plugin_exec "github.com/thegeeklab/wp-plugin-go/v3/exec"
"github.com/urfave/cli/v2"
)
const dockerBin = "/usr/local/bin/docker"
// Login defines Docker login parameters.
type Registry struct {
Address string // Docker registry address
Username string // Docker registry username
Password string // Docker registry password
Email string // Docker registry email
Config string // Docker Auth Config
}
// Build defines Docker build parameters.
type Build struct {
Ref string // Git commit ref
Branch string // Git repository branch
Containerfile string // Docker build Containerfile
Context string // Docker build context
TagsAuto bool // Docker build auto tag
TagsSuffix string // Docker build tags with suffix
Tags cli.StringSlice // Docker build tags
ExtraTags cli.StringSlice // Docker build tags including registry
Platforms cli.StringSlice // Docker build target platforms
Args map[string]string // Docker build args
ArgsEnv cli.StringSlice // Docker build args from env
Target string // Docker build target
Pull bool // Docker build pull
CacheFrom []string // Docker build cache-from
CacheTo string // Docker build cache-to
Compress bool // Docker build compress
Repo string // Docker build repository
NoCache bool // Docker build no-cache
AddHost cli.StringSlice // Docker build add-host
Quiet bool // Docker build quiet
Output string // Docker build output folder
NamedContext cli.StringSlice // Docker build named context
Labels cli.StringSlice // Docker build labels
Provenance string // Docker build provenance attestation
SBOM string // Docker build sbom attestation
Secrets []string // Docker build secrets
Dryrun bool // Docker build dryrun
}
// helper function to create the docker login command.
func (r *Registry) Login() *plugin_exec.Cmd {
args := []string{
"login",
"-u", r.Username,
"-p", r.Password,
}
if r.Email != "" {
args = append(args, "-e", r.Email)
}
args = append(args, r.Address)
cmd := plugin_exec.Command(dockerBin, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd
}
// helper function to create the docker info command.
func Version() *plugin_exec.Cmd {
cmd := plugin_exec.Command(dockerBin, "version")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd
}
// helper function to create the docker info command.
func Info() *plugin_exec.Cmd {
cmd := plugin_exec.Command(dockerBin, "info")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd
}
// helper function to create the docker build command.
func (b *Build) Run() *plugin_exec.Cmd {
args := []string{
"buildx",
"build",
"--rm=true",
"-f", b.Containerfile,
}
defaultBuildArgs := map[string]string{
"DOCKER_IMAGE_CREATED": time.Now().Format(time.RFC3339),
}
maps.Copy(b.Args, defaultBuildArgs)
args = append(args, b.Context)
if !b.Dryrun && b.Output == "" && len(b.Tags.Value()) > 0 {
args = append(args, "--push")
}
if b.Compress {
args = append(args, "--compress")
}
if b.Pull {
args = append(args, "--pull=true")
}
if b.NoCache {
args = append(args, "--no-cache")
}
for _, arg := range b.CacheFrom {
args = append(args, "--cache-from", arg)
}
if b.CacheTo != "" {
args = append(args, "--cache-to", b.CacheTo)
}
for _, arg := range b.ArgsEnv.Value() {
b.addArgFromEnv(arg)
}
for key, value := range b.Args {
args = append(args, "--build-arg", fmt.Sprintf("%s=%s", key, value))
}
for _, host := range b.AddHost.Value() {
args = append(args, "--add-host", host)
}
if b.Target != "" {
args = append(args, "--target", b.Target)
}
if b.Quiet {
args = append(args, "--quiet")
}
if b.Output != "" {
args = append(args, "--output", b.Output)
}
for _, arg := range b.NamedContext.Value() {
args = append(args, "--build-context", arg)
}
if len(b.Platforms.Value()) > 0 {
args = append(args, "--platform", strings.Join(b.Platforms.Value(), ","))
}
for _, arg := range b.Tags.Value() {
args = append(args, "-t", fmt.Sprintf("%s:%s", b.Repo, arg))
}
for _, arg := range b.ExtraTags.Value() {
args = append(args, "-t", arg)
}
for _, arg := range b.Labels.Value() {
args = append(args, "--label", arg)
}
if b.Provenance != "" {
args = append(args, "--provenance", b.Provenance)
}
if b.SBOM != "" {
args = append(args, "--sbom", b.SBOM)
}
for _, secret := range b.Secrets {
args = append(args, "--secret", secret)
}
cmd := plugin_exec.Command(dockerBin, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd
}
// helper function to add proxy values from the environment.
func (b *Build) AddProxyBuildArgs() {
b.addProxyValue("http_proxy")
b.addProxyValue("https_proxy")
b.addProxyValue("no_proxy")
}
// helper function to add the upper and lower case version of a proxy value.
func (b *Build) addProxyValue(key string) {
value := b.getProxyValue(key)
if value != "" && !b.hasProxyBuildArg(key) {
b.Args[key] = value
b.Args[strings.ToUpper(key)] = value
}
}
func (b *Build) addArgFromEnv(key string) {
if value, ok := b.Args[key]; ok && value != "" {
return
}
if value, ok := os.LookupEnv(key); ok && value != "" {
b.Args[key] = value
}
}
// helper function to get a proxy value from the environment.
//
// assumes that the upper and lower case versions of are the same.
func (b *Build) getProxyValue(key string) string {
value := os.Getenv(key)
if value != "" {
return value
}
return os.Getenv(strings.ToUpper(key))
}
// helper function that looks to see if a proxy value was set in the build args.
func (b *Build) hasProxyBuildArg(key string) bool {
if _, ok := b.Args[key]; ok {
return true
}
if _, ok := b.Args[strings.ToUpper(key)]; ok {
return true
}
return false
}