init
This commit is contained in:
commit
94ad55ac2e
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
buildx-manager
|
||||
test.yml
|
52
buildx/build.go
Normal file
52
buildx/build.go
Normal file
@ -0,0 +1,52 @@
|
||||
package buildx
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"git.tek.govt.hu/dowerx/buildx-manager/config"
|
||||
)
|
||||
|
||||
func RepoToCommands(repo *Repository) ([]Command, error) {
|
||||
cfg := config.GetConfig()
|
||||
|
||||
workdir, err := filepath.Abs(repo.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
globalArgs := make([]string, 0)
|
||||
for _, arg := range repo.GlobalArguments {
|
||||
globalArgs = append(globalArgs, "--build-arg", arg.Key+"="+arg.Value)
|
||||
}
|
||||
|
||||
commands := make([]Command, 0)
|
||||
|
||||
// builds
|
||||
for _, build := range repo.Builds {
|
||||
id := uid()
|
||||
|
||||
cmd := Command{Program: cfg.DockerExecutable, Arguments: []string{"buildx", "build", "-f", build.Dockerfile, "--platform", "linux/" + build.Architecture, "--tag", id, cfg.Action}, WorkingDirectory: workdir}
|
||||
|
||||
// args
|
||||
cmd.Arguments = append(cmd.Arguments, globalArgs...)
|
||||
for _, arg := range build.Arguments {
|
||||
cmd.Arguments = append(cmd.Arguments, "--build-arg", arg.Key+"="+arg.Value)
|
||||
}
|
||||
|
||||
// tags
|
||||
for _, tag := range build.Tags {
|
||||
addUniqueTag(id, repo.Library+"/"+tag)
|
||||
}
|
||||
|
||||
commands = append(commands, cmd)
|
||||
}
|
||||
|
||||
// tags
|
||||
for _, rtag := range repo.Tags {
|
||||
for _, tag := range rtag.Tags {
|
||||
addGroupTag(repo.Library+"/"+rtag.Name, repo.Library+"/"+tag)
|
||||
}
|
||||
}
|
||||
|
||||
return commands, err
|
||||
}
|
26
buildx/command.go
Normal file
26
buildx/command.go
Normal file
@ -0,0 +1,26 @@
|
||||
package buildx
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"git.tek.govt.hu/dowerx/buildx-manager/config"
|
||||
)
|
||||
|
||||
type Command struct {
|
||||
Program string
|
||||
Arguments []string
|
||||
WorkingDirectory string
|
||||
}
|
||||
|
||||
func (c *Command) Run() error {
|
||||
cmd := exec.Command(c.Program, c.Arguments...)
|
||||
cmd.Dir = c.WorkingDirectory
|
||||
|
||||
if config.GetConfig().Verbose {
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
}
|
||||
|
||||
return cmd.Run()
|
||||
}
|
105
buildx/load.go
Normal file
105
buildx/load.go
Normal file
@ -0,0 +1,105 @@
|
||||
package buildx
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/drone/envsubst"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func subst(ref *string) error {
|
||||
result, err := envsubst.EvalEnv(*ref)
|
||||
*ref = result
|
||||
return err
|
||||
}
|
||||
|
||||
func LoadJob(path string) (*Job, error) {
|
||||
var job Job
|
||||
|
||||
file, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(file, &job)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// envsubst
|
||||
|
||||
// registries
|
||||
for i := range job.Registries {
|
||||
if err = subst(&job.Registries[i]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// repos
|
||||
for i := range job.Repositories {
|
||||
// name
|
||||
if err = subst(&job.Repositories[i].Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// library
|
||||
if err = subst(&job.Repositories[i].Library); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// path
|
||||
if err = subst(&job.Repositories[i].Path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// global args
|
||||
for j := range job.Repositories[i].GlobalArguments {
|
||||
// key
|
||||
if err = subst(&job.Repositories[i].GlobalArguments[j].Key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// value
|
||||
if err = subst(&job.Repositories[i].GlobalArguments[j].Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// tags
|
||||
for j := range job.Repositories[i].Tags {
|
||||
// name
|
||||
if err = subst(&job.Repositories[i].Tags[j].Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// tags
|
||||
for u := range job.Repositories[i].Tags[j].Tags {
|
||||
if err = subst(&job.Repositories[i].Tags[j].Tags[u]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// builds
|
||||
for j := range job.Repositories[i].Builds {
|
||||
// arch
|
||||
if err = subst(&job.Repositories[i].Builds[j].Architecture); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// dockerfile
|
||||
if err = subst(&job.Repositories[i].Builds[j].Dockerfile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// tags
|
||||
for u := range job.Repositories[i].Builds[j].Tags {
|
||||
if err = subst(&job.Repositories[i].Builds[j].Tags[u]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &job, err
|
||||
}
|
62
buildx/tag.go
Normal file
62
buildx/tag.go
Normal file
@ -0,0 +1,62 @@
|
||||
package buildx
|
||||
|
||||
import (
|
||||
"git.tek.govt.hu/dowerx/buildx-manager/config"
|
||||
"golang.org/x/exp/rand"
|
||||
)
|
||||
|
||||
var uniqueTags map[string][]string = make(map[string][]string)
|
||||
var groupTags map[string][]string = make(map[string][]string)
|
||||
var runes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
|
||||
func uid() string {
|
||||
for {
|
||||
id := make([]rune, 16)
|
||||
for i := range id {
|
||||
id[i] = runes[rand.Intn(len(runes))]
|
||||
}
|
||||
|
||||
_, exists := uniqueTags[string(id)]
|
||||
if !exists {
|
||||
return string(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addUniqueTag(id string, tag string) {
|
||||
uniqueTags[id] = append(uniqueTags[id], tag)
|
||||
}
|
||||
|
||||
func addGroupTag(id string, tag string) {
|
||||
groupTags[id] = append(groupTags[id], tag)
|
||||
}
|
||||
|
||||
func TagsToCommands(registries []string) []Command {
|
||||
cfg := config.GetConfig()
|
||||
|
||||
commands := make([]Command, 0)
|
||||
|
||||
for _, registry := range registries {
|
||||
|
||||
// unique tags
|
||||
for id, tags := range uniqueTags {
|
||||
for _, tag := range tags {
|
||||
cmd := Command{Program: cfg.DockerExecutable, Arguments: []string{"buildx", "imagetools", "create", "-t", registry + "/" + tag, id}}
|
||||
commands = append(commands, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
// group tags
|
||||
for newTag, tags := range groupTags {
|
||||
cmd := Command{Program: cfg.DockerExecutable, Arguments: []string{"buildx", "imagetools", "create", "-t", registry + "/" + newTag}}
|
||||
|
||||
for _, tag := range tags {
|
||||
cmd.Arguments = append(cmd.Arguments, registry+"/"+tag)
|
||||
}
|
||||
|
||||
commands = append(commands, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
return commands
|
||||
}
|
38
buildx/types.go
Normal file
38
buildx/types.go
Normal file
@ -0,0 +1,38 @@
|
||||
package buildx
|
||||
|
||||
const (
|
||||
AMD64 string = "amd64"
|
||||
ARM64 string = "arm64"
|
||||
ARMv7 string = "armv7"
|
||||
)
|
||||
|
||||
type Argument struct {
|
||||
Key string `yaml:"key"`
|
||||
Value string `yaml:"value"`
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
Name string `yaml:"name"`
|
||||
Tags []string `yaml:"tags"`
|
||||
}
|
||||
|
||||
type Build struct {
|
||||
Architecture string `yaml:"arch"`
|
||||
Dockerfile string `yaml:"dockerfile" default:"Dockerfile"`
|
||||
Tags []string `yaml:"tags"`
|
||||
Arguments []Argument `yaml:"args"`
|
||||
}
|
||||
|
||||
type Repository struct {
|
||||
Name string `yaml:"name"`
|
||||
Library string `yaml:"library"`
|
||||
Path string `yaml:"path"`
|
||||
GlobalArguments []Argument `yaml:"args"`
|
||||
Tags []Tag `yaml:"tags"`
|
||||
Builds []Build `yaml:"builds"`
|
||||
}
|
||||
|
||||
type Job struct {
|
||||
Registries []string `yaml:"registries"`
|
||||
Repositories []Repository `yaml:"repos"`
|
||||
}
|
41
config/config.go
Normal file
41
config/config.go
Normal file
@ -0,0 +1,41 @@
|
||||
package config
|
||||
|
||||
import "flag"
|
||||
|
||||
var config *Config
|
||||
|
||||
type Config struct {
|
||||
File string
|
||||
Action string
|
||||
Verbose bool
|
||||
Parallel bool
|
||||
Dryrun bool
|
||||
DockerExecutable string
|
||||
}
|
||||
|
||||
func GetConfig() Config {
|
||||
if config != nil {
|
||||
return *config
|
||||
}
|
||||
|
||||
config = &Config{}
|
||||
|
||||
flag.StringVar(&config.File, "f", "", "job definition")
|
||||
flag.StringVar(&config.Action, "a", "", "action")
|
||||
flag.BoolVar(&config.Verbose, "v", false, "verbose")
|
||||
flag.BoolVar(&config.Parallel, "p", false, "parallel")
|
||||
flag.BoolVar(&config.Dryrun, "dry", false, "dryrun")
|
||||
flag.StringVar(&config.DockerExecutable, "docker", "docker", "docker executable")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if config.File == "" {
|
||||
panic("missing job definition")
|
||||
}
|
||||
|
||||
if config.Action != "load" && config.Action != "push" {
|
||||
panic("action must be \"load\" or \"push\"")
|
||||
}
|
||||
|
||||
return *config
|
||||
}
|
10
go.mod
Normal file
10
go.mod
Normal file
@ -0,0 +1,10 @@
|
||||
module git.tek.govt.hu/dowerx/buildx-manager
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/drone/envsubst v1.0.3 // indirect
|
||||
github.com/sanity-io/litter v1.5.5 // indirect
|
||||
golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
13
go.sum
Normal file
13
go.sum
Normal file
@ -0,0 +1,13 @@
|
||||
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g=
|
||||
github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 h1:1wqE9dj9NpSm04INVsJhhEUzhuDVjbcyKH91sVyPATw=
|
||||
golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
88
main.go
Normal file
88
main.go
Normal file
@ -0,0 +1,88 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.tek.govt.hu/dowerx/buildx-manager/buildx"
|
||||
"git.tek.govt.hu/dowerx/buildx-manager/config"
|
||||
"github.com/sanity-io/litter"
|
||||
"golang.org/x/exp/rand"
|
||||
)
|
||||
|
||||
func main() {
|
||||
rand.Seed(uint64(time.Now().Unix()))
|
||||
|
||||
cfg := config.GetConfig()
|
||||
|
||||
job, err := buildx.LoadJob(cfg.File)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
buildCommands := make([]buildx.Command, 0)
|
||||
|
||||
for _, repo := range job.Repositories {
|
||||
cmd, err := buildx.RepoToCommands(&repo)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
buildCommands = append(buildCommands, cmd...)
|
||||
}
|
||||
|
||||
tagCommands := buildx.TagsToCommands(job.Registries)
|
||||
|
||||
if cfg.Verbose {
|
||||
fmt.Println("config:")
|
||||
litter.Dump(cfg)
|
||||
|
||||
fmt.Println("\njob loaded:")
|
||||
litter.Dump(job)
|
||||
|
||||
fmt.Println("\nbuild commands:", len(buildCommands))
|
||||
litter.Dump(buildCommands)
|
||||
|
||||
fmt.Println("\ntag commands:", len(tagCommands))
|
||||
litter.Dump(tagCommands)
|
||||
}
|
||||
|
||||
if cfg.Dryrun {
|
||||
return
|
||||
}
|
||||
|
||||
// build
|
||||
if cfg.Parallel {
|
||||
errors := make(chan error)
|
||||
|
||||
// start builds
|
||||
for _, cmd := range buildCommands {
|
||||
go func() {
|
||||
errors <- cmd.Run()
|
||||
}()
|
||||
}
|
||||
|
||||
// wait for builds
|
||||
for i := 0; i < len(buildCommands); i++ {
|
||||
for err := range errors {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, cmd := range buildCommands {
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tag
|
||||
for _, cmd := range tagCommands {
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user