mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-05-19 01:47:43 +08:00
bake: load override
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
parent
5c29e6e26e
commit
dc4b4c36bd
91
bake/bake.go
91
bake/bake.go
@ -894,19 +894,17 @@ func (t *Target) AddOverrides(overrides map[string]Override) error {
|
|||||||
}
|
}
|
||||||
t.Pull = &pull
|
t.Pull = &pull
|
||||||
case "push":
|
case "push":
|
||||||
_, err := strconv.ParseBool(value)
|
push, err := strconv.ParseBool(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Errorf("invalid value %s for boolean key push", value)
|
return errors.Errorf("invalid value %s for boolean key push", value)
|
||||||
}
|
}
|
||||||
if len(t.Outputs) == 0 {
|
t.Outputs = setPushOverride(t.Outputs, push)
|
||||||
t.Outputs = append(t.Outputs, "type=image,push=true")
|
case "load":
|
||||||
} else {
|
load, err := strconv.ParseBool(value)
|
||||||
for i, output := range t.Outputs {
|
if err != nil {
|
||||||
if typ := parseOutputType(output); typ == "image" || typ == "registry" {
|
return errors.Errorf("invalid value %s for boolean key load", value)
|
||||||
t.Outputs[i] = t.Outputs[i] + ",push=" + value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
t.Outputs = setLoadOverride(t.Outputs, load)
|
||||||
default:
|
default:
|
||||||
return errors.Errorf("unknown key: %s", keys[0])
|
return errors.Errorf("unknown key: %s", keys[0])
|
||||||
}
|
}
|
||||||
@ -1394,23 +1392,90 @@ func removeAttestDupes(s []string) []string {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseOutputType(str string) string {
|
func parseOutput(str string) map[string]string {
|
||||||
csvReader := csv.NewReader(strings.NewReader(str))
|
csvReader := csv.NewReader(strings.NewReader(str))
|
||||||
fields, err := csvReader.Read()
|
fields, err := csvReader.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return nil
|
||||||
}
|
}
|
||||||
|
res := map[string]string{}
|
||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
parts := strings.SplitN(field, "=", 2)
|
parts := strings.SplitN(field, "=", 2)
|
||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
if parts[0] == "type" {
|
res[parts[0]] = parts[1]
|
||||||
return parts[1]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseOutputType(str string) string {
|
||||||
|
if out := parseOutput(str); out != nil {
|
||||||
|
if v, ok := out["type"]; ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setPushOverride(outputs []string, push bool) []string {
|
||||||
|
var out []string
|
||||||
|
setPush := true
|
||||||
|
for _, output := range outputs {
|
||||||
|
typ := parseOutputType(output)
|
||||||
|
if typ == "image" || typ == "registry" {
|
||||||
|
// no need to set push if image or registry types already defined
|
||||||
|
setPush = false
|
||||||
|
if typ == "registry" {
|
||||||
|
if !push {
|
||||||
|
// don't set registry output if "push" is false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// no need to set "push" attribute to true for registry
|
||||||
|
out = append(out, output)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, output+",push="+strconv.FormatBool(push))
|
||||||
|
} else {
|
||||||
|
if typ != "docker" {
|
||||||
|
// if there is any output that is not docker, don't set "push"
|
||||||
|
setPush = false
|
||||||
|
}
|
||||||
|
out = append(out, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if push && setPush {
|
||||||
|
out = append(out, "type=image,push=true")
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func setLoadOverride(outputs []string, load bool) []string {
|
||||||
|
if !load {
|
||||||
|
return outputs
|
||||||
|
}
|
||||||
|
setLoad := true
|
||||||
|
for _, output := range outputs {
|
||||||
|
if typ := parseOutputType(output); typ == "docker" {
|
||||||
|
if v := parseOutput(output); v != nil {
|
||||||
|
// dest set means we want to output as tar so don't set load
|
||||||
|
if _, ok := v["dest"]; !ok {
|
||||||
|
setLoad = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if typ != "image" && typ != "registry" && typ != "oci" {
|
||||||
|
// if there is any output that is not an image, registry
|
||||||
|
// or oci, don't set "load" similar to push override
|
||||||
|
setLoad = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if setLoad {
|
||||||
|
outputs = append(outputs, "type=docker")
|
||||||
|
}
|
||||||
|
return outputs
|
||||||
|
}
|
||||||
|
|
||||||
func validateTargetName(name string) error {
|
func validateTargetName(name string) error {
|
||||||
if !targetNamePattern.MatchString(name) {
|
if !targetNamePattern.MatchString(name) {
|
||||||
return errors.Errorf("only %q are allowed", validTargetNameChars)
|
return errors.Errorf("only %q are allowed", validTargetNameChars)
|
||||||
|
@ -217,8 +217,20 @@ target "webapp" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPushOverride(t *testing.T) {
|
func TestPushOverride(t *testing.T) {
|
||||||
t.Parallel()
|
t.Run("empty output", func(t *testing.T) {
|
||||||
|
fp := File{
|
||||||
|
Name: "docker-bake.hcl",
|
||||||
|
Data: []byte(
|
||||||
|
`target "app" {
|
||||||
|
}`),
|
||||||
|
}
|
||||||
|
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=true"}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(m["app"].Outputs))
|
||||||
|
require.Equal(t, "type=image,push=true", m["app"].Outputs[0])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("type image", func(t *testing.T) {
|
||||||
fp := File{
|
fp := File{
|
||||||
Name: "docker-bake.hcl",
|
Name: "docker-bake.hcl",
|
||||||
Data: []byte(
|
Data: []byte(
|
||||||
@ -226,41 +238,55 @@ func TestPushOverride(t *testing.T) {
|
|||||||
output = ["type=image,compression=zstd"]
|
output = ["type=image,compression=zstd"]
|
||||||
}`),
|
}`),
|
||||||
}
|
}
|
||||||
ctx := context.TODO()
|
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=true"}, nil)
|
||||||
m, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"*.push=true"}, nil)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, 1, len(m["app"].Outputs))
|
require.Equal(t, 1, len(m["app"].Outputs))
|
||||||
require.Equal(t, "type=image,compression=zstd,push=true", m["app"].Outputs[0])
|
require.Equal(t, "type=image,compression=zstd,push=true", m["app"].Outputs[0])
|
||||||
|
})
|
||||||
|
|
||||||
fp = File{
|
t.Run("type image push false", func(t *testing.T) {
|
||||||
|
fp := File{
|
||||||
Name: "docker-bake.hcl",
|
Name: "docker-bake.hcl",
|
||||||
Data: []byte(
|
Data: []byte(
|
||||||
`target "app" {
|
`target "app" {
|
||||||
output = ["type=image,compression=zstd"]
|
output = ["type=image,compression=zstd"]
|
||||||
}`),
|
}`),
|
||||||
}
|
}
|
||||||
ctx = context.TODO()
|
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=false"}, nil)
|
||||||
m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"*.push=false"}, nil)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, 1, len(m["app"].Outputs))
|
require.Equal(t, 1, len(m["app"].Outputs))
|
||||||
require.Equal(t, "type=image,compression=zstd,push=false", m["app"].Outputs[0])
|
require.Equal(t, "type=image,compression=zstd,push=false", m["app"].Outputs[0])
|
||||||
|
})
|
||||||
|
|
||||||
fp = File{
|
t.Run("type registry", func(t *testing.T) {
|
||||||
|
fp := File{
|
||||||
Name: "docker-bake.hcl",
|
Name: "docker-bake.hcl",
|
||||||
Data: []byte(
|
Data: []byte(
|
||||||
`target "app" {
|
`target "app" {
|
||||||
|
output = ["type=registry"]
|
||||||
}`),
|
}`),
|
||||||
}
|
}
|
||||||
ctx = context.TODO()
|
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=true"}, nil)
|
||||||
m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"*.push=true"}, nil)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, 1, len(m["app"].Outputs))
|
require.Equal(t, 1, len(m["app"].Outputs))
|
||||||
require.Equal(t, "type=image,push=true", m["app"].Outputs[0])
|
require.Equal(t, "type=registry", m["app"].Outputs[0])
|
||||||
|
})
|
||||||
|
|
||||||
fp = File{
|
t.Run("type registry push false", func(t *testing.T) {
|
||||||
|
fp := File{
|
||||||
|
Name: "docker-bake.hcl",
|
||||||
|
Data: []byte(
|
||||||
|
`target "app" {
|
||||||
|
output = ["type=registry"]
|
||||||
|
}`),
|
||||||
|
}
|
||||||
|
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=false"}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 0, len(m["app"].Outputs))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("type local and empty target", func(t *testing.T) {
|
||||||
|
fp := File{
|
||||||
Name: "docker-bake.hcl",
|
Name: "docker-bake.hcl",
|
||||||
Data: []byte(
|
Data: []byte(
|
||||||
`target "foo" {
|
`target "foo" {
|
||||||
@ -269,15 +295,174 @@ func TestPushOverride(t *testing.T) {
|
|||||||
target "bar" {
|
target "bar" {
|
||||||
}`),
|
}`),
|
||||||
}
|
}
|
||||||
ctx = context.TODO()
|
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"foo", "bar"}, []string{"*.push=true"}, nil)
|
||||||
m, _, err = ReadTargets(ctx, []File{fp}, []string{"foo", "bar"}, []string{"*.push=true"}, nil)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, 2, len(m))
|
require.Equal(t, 2, len(m))
|
||||||
require.Equal(t, 1, len(m["foo"].Outputs))
|
require.Equal(t, 1, len(m["foo"].Outputs))
|
||||||
require.Equal(t, []string{"type=local,dest=out"}, m["foo"].Outputs)
|
require.Equal(t, []string{"type=local,dest=out"}, m["foo"].Outputs)
|
||||||
require.Equal(t, 1, len(m["bar"].Outputs))
|
require.Equal(t, 1, len(m["bar"].Outputs))
|
||||||
require.Equal(t, []string{"type=image,push=true"}, m["bar"].Outputs)
|
require.Equal(t, []string{"type=image,push=true"}, m["bar"].Outputs)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadOverride(t *testing.T) {
|
||||||
|
t.Run("empty output", func(t *testing.T) {
|
||||||
|
fp := File{
|
||||||
|
Name: "docker-bake.hcl",
|
||||||
|
Data: []byte(
|
||||||
|
`target "app" {
|
||||||
|
}`),
|
||||||
|
}
|
||||||
|
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(m["app"].Outputs))
|
||||||
|
require.Equal(t, "type=docker", m["app"].Outputs[0])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("type docker", func(t *testing.T) {
|
||||||
|
fp := File{
|
||||||
|
Name: "docker-bake.hcl",
|
||||||
|
Data: []byte(
|
||||||
|
`target "app" {
|
||||||
|
output = ["type=docker"]
|
||||||
|
}`),
|
||||||
|
}
|
||||||
|
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(m["app"].Outputs))
|
||||||
|
require.Equal(t, []string{"type=docker"}, m["app"].Outputs)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("type image", func(t *testing.T) {
|
||||||
|
fp := File{
|
||||||
|
Name: "docker-bake.hcl",
|
||||||
|
Data: []byte(
|
||||||
|
`target "app" {
|
||||||
|
output = ["type=image"]
|
||||||
|
}`),
|
||||||
|
}
|
||||||
|
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(m["app"].Outputs))
|
||||||
|
require.Equal(t, []string{"type=image", "type=docker"}, m["app"].Outputs)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("type image load false", func(t *testing.T) {
|
||||||
|
fp := File{
|
||||||
|
Name: "docker-bake.hcl",
|
||||||
|
Data: []byte(
|
||||||
|
`target "app" {
|
||||||
|
output = ["type=image"]
|
||||||
|
}`),
|
||||||
|
}
|
||||||
|
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=false"}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(m["app"].Outputs))
|
||||||
|
require.Equal(t, []string{"type=image"}, m["app"].Outputs)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("type registry", func(t *testing.T) {
|
||||||
|
fp := File{
|
||||||
|
Name: "docker-bake.hcl",
|
||||||
|
Data: []byte(
|
||||||
|
`target "app" {
|
||||||
|
output = ["type=registry"]
|
||||||
|
}`),
|
||||||
|
}
|
||||||
|
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(m["app"].Outputs))
|
||||||
|
require.Equal(t, []string{"type=registry", "type=docker"}, m["app"].Outputs)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("type oci", func(t *testing.T) {
|
||||||
|
fp := File{
|
||||||
|
Name: "docker-bake.hcl",
|
||||||
|
Data: []byte(
|
||||||
|
`target "app" {
|
||||||
|
output = ["type=oci,dest=out"]
|
||||||
|
}`),
|
||||||
|
}
|
||||||
|
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(m["app"].Outputs))
|
||||||
|
require.Equal(t, []string{"type=oci,dest=out", "type=docker"}, m["app"].Outputs)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("type docker with dest", func(t *testing.T) {
|
||||||
|
fp := File{
|
||||||
|
Name: "docker-bake.hcl",
|
||||||
|
Data: []byte(
|
||||||
|
`target "app" {
|
||||||
|
output = ["type=docker,dest=out"]
|
||||||
|
}`),
|
||||||
|
}
|
||||||
|
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(m["app"].Outputs))
|
||||||
|
require.Equal(t, []string{"type=docker,dest=out", "type=docker"}, m["app"].Outputs)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("type local and empty target", func(t *testing.T) {
|
||||||
|
fp := File{
|
||||||
|
Name: "docker-bake.hcl",
|
||||||
|
Data: []byte(
|
||||||
|
`target "foo" {
|
||||||
|
output = [ "type=local,dest=out" ]
|
||||||
|
}
|
||||||
|
target "bar" {
|
||||||
|
}`),
|
||||||
|
}
|
||||||
|
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"foo", "bar"}, []string{"*.load=true"}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(m))
|
||||||
|
require.Equal(t, 1, len(m["foo"].Outputs))
|
||||||
|
require.Equal(t, []string{"type=local,dest=out"}, m["foo"].Outputs)
|
||||||
|
require.Equal(t, 1, len(m["bar"].Outputs))
|
||||||
|
require.Equal(t, []string{"type=docker"}, m["bar"].Outputs)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadAndPushOverride(t *testing.T) {
|
||||||
|
t.Run("type local and empty target", func(t *testing.T) {
|
||||||
|
fp := File{
|
||||||
|
Name: "docker-bake.hcl",
|
||||||
|
Data: []byte(
|
||||||
|
`target "foo" {
|
||||||
|
output = [ "type=local,dest=out" ]
|
||||||
|
}
|
||||||
|
target "bar" {
|
||||||
|
}`),
|
||||||
|
}
|
||||||
|
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"foo", "bar"}, []string{"*.load=true", "*.push=true"}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(m))
|
||||||
|
|
||||||
|
require.Equal(t, 1, len(m["foo"].Outputs))
|
||||||
|
sort.Strings(m["foo"].Outputs)
|
||||||
|
require.Equal(t, []string{"type=local,dest=out"}, m["foo"].Outputs)
|
||||||
|
|
||||||
|
require.Equal(t, 2, len(m["bar"].Outputs))
|
||||||
|
sort.Strings(m["bar"].Outputs)
|
||||||
|
require.Equal(t, []string{"type=docker", "type=image,push=true"}, m["bar"].Outputs)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("type registry", func(t *testing.T) {
|
||||||
|
fp := File{
|
||||||
|
Name: "docker-bake.hcl",
|
||||||
|
Data: []byte(
|
||||||
|
`target "foo" {
|
||||||
|
output = [ "type=registry" ]
|
||||||
|
}`),
|
||||||
|
}
|
||||||
|
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"foo"}, []string{"*.load=true", "*.push=true"}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(m))
|
||||||
|
|
||||||
|
require.Equal(t, 2, len(m["foo"].Outputs))
|
||||||
|
sort.Strings(m["foo"].Outputs)
|
||||||
|
require.Equal(t, []string{"type=docker", "type=registry"}, m["foo"].Outputs)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadTargetsCompose(t *testing.T) {
|
func TestReadTargetsCompose(t *testing.T) {
|
||||||
|
@ -75,7 +75,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
|||||||
overrides = append(overrides, "*.push=true")
|
overrides = append(overrides, "*.push=true")
|
||||||
}
|
}
|
||||||
if in.exportLoad {
|
if in.exportLoad {
|
||||||
overrides = append(overrides, "*.output=type=docker")
|
overrides = append(overrides, "*.load=true")
|
||||||
}
|
}
|
||||||
if cFlags.noCache != nil {
|
if cFlags.noCache != nil {
|
||||||
overrides = append(overrides, fmt.Sprintf("*.no-cache=%t", *cFlags.noCache))
|
overrides = append(overrides, fmt.Sprintf("*.no-cache=%t", *cFlags.noCache))
|
||||||
|
@ -162,6 +162,7 @@ You can override the following fields:
|
|||||||
* `context`
|
* `context`
|
||||||
* `dockerfile`
|
* `dockerfile`
|
||||||
* `labels`
|
* `labels`
|
||||||
|
* `load`
|
||||||
* `no-cache`
|
* `no-cache`
|
||||||
* `no-cache-filter`
|
* `no-cache-filter`
|
||||||
* `output`
|
* `output`
|
||||||
|
@ -814,11 +814,11 @@ target "default" {
|
|||||||
outb, err := cmd.CombinedOutput()
|
outb, err := cmd.CombinedOutput()
|
||||||
require.NoError(t, err, string(outb))
|
require.NoError(t, err, string(outb))
|
||||||
|
|
||||||
// TODO: test registry when --load case fixed for bake (currently overrides --push)
|
// test registry
|
||||||
//desc, provider, err := contentutil.ProviderFromRef(target)
|
desc, provider, err := contentutil.ProviderFromRef(target)
|
||||||
//require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
//_, err = testutil.ReadImages(sb.Context(), provider, desc)
|
_, err = testutil.ReadImages(sb.Context(), provider, desc)
|
||||||
//require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// test docker store
|
// test docker store
|
||||||
cmd = dockerCmd(sb, withArgs("image", "inspect", target))
|
cmd = dockerCmd(sb, withArgs("image", "inspect", target))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user