mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-07-10 13:37:08 +08:00
bake: initial set of composable bake attributes
This allows using either the csv syntax or object syntax to specify certain attributes. This applies to the following fields: - output - cache-from - cache-to - secret - ssh There are still some remaining fields to translate. Specifically ulimits, annotations, and attest. Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
This commit is contained in:
@ -2,6 +2,8 @@ package buildflags
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"maps"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
@ -9,66 +11,154 @@ import (
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tonistiigi/go-csvvalue"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
jsoncty "github.com/zclconf/go-cty/cty/json"
|
||||
)
|
||||
|
||||
type CacheOptionsEntry struct {
|
||||
Type string `json:"type"`
|
||||
Attrs map[string]string `json:"attrs,omitempty"`
|
||||
}
|
||||
|
||||
func (e *CacheOptionsEntry) Equal(other *CacheOptionsEntry) bool {
|
||||
if e.Type != other.Type {
|
||||
return false
|
||||
}
|
||||
return maps.Equal(e.Attrs, other.Attrs)
|
||||
}
|
||||
|
||||
func (e *CacheOptionsEntry) String() string {
|
||||
// Special registry syntax.
|
||||
if e.Type == "registry" && len(e.Attrs) == 1 {
|
||||
if ref, ok := e.Attrs["ref"]; ok {
|
||||
return ref
|
||||
}
|
||||
}
|
||||
|
||||
var b csvBuilder
|
||||
if e.Type != "" {
|
||||
b.Write("type", e.Type)
|
||||
}
|
||||
if len(e.Attrs) > 0 {
|
||||
b.WriteAttributes(e.Attrs)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func (e *CacheOptionsEntry) ToPB() *controllerapi.CacheOptionsEntry {
|
||||
return &controllerapi.CacheOptionsEntry{
|
||||
Type: e.Type,
|
||||
Attrs: maps.Clone(e.Attrs),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *CacheOptionsEntry) MarshalJSON() ([]byte, error) {
|
||||
m := maps.Clone(e.Attrs)
|
||||
if m == nil {
|
||||
m = map[string]string{}
|
||||
}
|
||||
m["type"] = e.Type
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
func (e *CacheOptionsEntry) UnmarshalJSON(data []byte) error {
|
||||
var m map[string]string
|
||||
if err := json.Unmarshal(data, &m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.Type = m["type"]
|
||||
delete(m, "type")
|
||||
|
||||
e.Attrs = m
|
||||
return e.validate(data)
|
||||
}
|
||||
|
||||
func (e *CacheOptionsEntry) IsActive() bool {
|
||||
// Always active if not gha.
|
||||
if e.Type != "gha" {
|
||||
return true
|
||||
}
|
||||
return e.Attrs["token"] != "" && e.Attrs["url"] != ""
|
||||
}
|
||||
|
||||
func (e *CacheOptionsEntry) UnmarshalText(text []byte) error {
|
||||
in := string(text)
|
||||
fields, err := csvvalue.Fields(in, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(fields) == 1 && !strings.Contains(fields[0], "=") {
|
||||
e.Type = "registry"
|
||||
e.Attrs = map[string]string{"ref": fields[0]}
|
||||
return nil
|
||||
}
|
||||
|
||||
e.Type = ""
|
||||
e.Attrs = map[string]string{}
|
||||
|
||||
for _, field := range fields {
|
||||
parts := strings.SplitN(field, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return errors.Errorf("invalid value %s", field)
|
||||
}
|
||||
key := strings.ToLower(parts[0])
|
||||
value := parts[1]
|
||||
switch key {
|
||||
case "type":
|
||||
e.Type = value
|
||||
default:
|
||||
e.Attrs[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
if e.Type == "" {
|
||||
return errors.Errorf("type required form> %q", in)
|
||||
}
|
||||
addGithubToken(e)
|
||||
addAwsCredentials(e)
|
||||
return e.validate(text)
|
||||
}
|
||||
|
||||
func (e *CacheOptionsEntry) validate(gv interface{}) error {
|
||||
if e.Type == "" {
|
||||
var text []byte
|
||||
switch gv := gv.(type) {
|
||||
case []byte:
|
||||
text = gv
|
||||
case string:
|
||||
text = []byte(gv)
|
||||
case cty.Value:
|
||||
text, _ = jsoncty.Marshal(gv, gv.Type())
|
||||
default:
|
||||
text, _ = json.Marshal(gv)
|
||||
}
|
||||
return errors.Errorf("type required form> %q", string(text))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseCacheEntry(in []string) ([]*controllerapi.CacheOptionsEntry, error) {
|
||||
outs := make([]*controllerapi.CacheOptionsEntry, 0, len(in))
|
||||
for _, in := range in {
|
||||
fields, err := csvvalue.Fields(in, nil)
|
||||
if err != nil {
|
||||
var out CacheOptionsEntry
|
||||
if err := out.UnmarshalText([]byte(in)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isRefOnlyFormat(fields) {
|
||||
for _, field := range fields {
|
||||
outs = append(outs, &controllerapi.CacheOptionsEntry{
|
||||
Type: "registry",
|
||||
Attrs: map[string]string{"ref": field},
|
||||
})
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
out := controllerapi.CacheOptionsEntry{
|
||||
Attrs: map[string]string{},
|
||||
}
|
||||
for _, field := range fields {
|
||||
parts := strings.SplitN(field, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, errors.Errorf("invalid value %s", field)
|
||||
}
|
||||
key := strings.ToLower(parts[0])
|
||||
value := parts[1]
|
||||
switch key {
|
||||
case "type":
|
||||
out.Type = value
|
||||
default:
|
||||
out.Attrs[key] = value
|
||||
}
|
||||
}
|
||||
if out.Type == "" {
|
||||
return nil, errors.Errorf("type required form> %q", in)
|
||||
}
|
||||
if !addGithubToken(&out) {
|
||||
if !out.IsActive() {
|
||||
// Skip inactive cache entries.
|
||||
continue
|
||||
}
|
||||
addAwsCredentials(&out)
|
||||
outs = append(outs, &out)
|
||||
outs = append(outs, out.ToPB())
|
||||
}
|
||||
return outs, nil
|
||||
}
|
||||
|
||||
func isRefOnlyFormat(in []string) bool {
|
||||
for _, v := range in {
|
||||
if strings.Contains(v, "=") {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func addGithubToken(ci *controllerapi.CacheOptionsEntry) bool {
|
||||
func addGithubToken(ci *CacheOptionsEntry) {
|
||||
if ci.Type != "gha" {
|
||||
return true
|
||||
return
|
||||
}
|
||||
if _, ok := ci.Attrs["token"]; !ok {
|
||||
if v, ok := os.LookupEnv("ACTIONS_RUNTIME_TOKEN"); ok {
|
||||
@ -80,10 +170,9 @@ func addGithubToken(ci *controllerapi.CacheOptionsEntry) bool {
|
||||
ci.Attrs["url"] = v
|
||||
}
|
||||
}
|
||||
return ci.Attrs["token"] != "" && ci.Attrs["url"] != ""
|
||||
}
|
||||
|
||||
func addAwsCredentials(ci *controllerapi.CacheOptionsEntry) {
|
||||
func addAwsCredentials(ci *CacheOptionsEntry) {
|
||||
if ci.Type != "s3" {
|
||||
return
|
||||
}
|
||||
|
Reference in New Issue
Block a user