chore: update moby/buildkit to v0.20.1 (#519)

**Description:**  
This PR updates the `moby/buildkit` dependency to `v0.20.1` in order to
remain compatible with recent changes required by GitHub Actions (GHA)
caching.

During the upgrade, several upstream behavioral changes required forking
and restoring legacy logic to maintain compatibility:

- Introduced `containsGithubToken` to replicate logic that was
previously in `buildflags.ParseCacheEntry`, which is now removed. This
check ensures we return `nil` instead of a zero-value object.
- Forked the previous implementation of `ParseExports`, as its upstream
logic changed significantly. This ensures short-term compatibility for
release before the GHA deprecation deadline.
- A follow-up issue will be created to improve this forked logic and
align more closely with upstream behavior.

🚨 **Fixes critical issue:**  
This change addresses a high-priority issue where GitHub Actions cache
will stop working due to the upcoming deprecation of the legacy caching
service (effective **April 15, 2025**).

Recent build failures showed errors like:
> *"This legacy service is shutting down, effective April 15, 2025.
Migrate to the new service ASAP. For more information:
https://gh.io/gha-cache-sunset"*

Root cause was identified in #515, where using `buildx >= 0.21.0` is
required.
This repo was previously using `buildx 0.18.0`.

Closes: #515
This commit is contained in:
Ramon Quitales
2025-04-11 13:42:47 -07:00
committed by GitHub
parent 0b5c155cad
commit f83b7a0a44
8 changed files with 279 additions and 158 deletions

View File

@@ -472,7 +472,23 @@ func (c CacheFrom) validate(preview bool) (*controllerapi.CacheOptionsEntry, err
// environment variables set. Ignore the cacheFrom entry in this case.
return nil, nil
}
return parsed[0], nil
pb := parsed[0].ToPB()
if !containsGithubToken(pb) {
pb = nil
}
return pb, nil
}
// containsGithubToken checks if the GitHub token is set in the cache entry.
// If it is not a GHA cache entry, it will return true.
// This is to maintain backwards compatibility with the old buildx behaviour.
func containsGithubToken(ci *controllerapi.CacheOptionsEntry) bool {
if ci.Type != "gha" {
return true
}
return ci.Attrs["token"] != "" && ci.Attrs["url"] != ""
}
// CacheToInline embeds cache information directly into an image.
@@ -680,7 +696,13 @@ func (c CacheTo) validate(preview bool) (*controllerapi.CacheOptionsEntry, error
// environment variables set. Ignore the cacheTo entry in this case.
return nil, nil
}
return parsed[0], nil
pb := parsed[0].ToPB()
if !containsGithubToken(pb) {
pb = nil
}
return pb, nil
}
// CacheMode controls the complexity of exported cache manifests.

View File

@@ -218,7 +218,7 @@ func (c *cli) Build(
Session: []session.Attachable{
ssh,
authprovider.NewDockerAuthProvider(c.ConfigFile(), nil),
authprovider.NewDockerAuthProvider(authprovider.DockerAuthProviderConfig{ConfigFile: c.ConfigFile()}),
build.Secrets(),
},
},

View File

@@ -22,7 +22,8 @@ import (
"strings"
controllerapi "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/util/buildflags"
"github.com/moby/buildkit/client"
"github.com/tonistiigi/go-csvvalue"
"github.com/pulumi/pulumi-go-provider/infer"
)
@@ -109,7 +110,7 @@ func (e Export) String() string {
// pushed returns true if the export would result in a registry push.
func (e Export) pushed() bool {
if e.Raw != "" {
exp, err := buildflags.ParseExports([]string{e.Raw.String()})
exp, err := parseExports([]string{e.Raw.String()})
if err != nil {
return false
}
@@ -124,14 +125,86 @@ func (e Export) pushed() bool {
return false
}
// parseExports is forked from docker/buildx@v0.18.0 from util/buildflags/export.go
// to maintain the old logic. This is to get a working version of the provider with
// the latest buildx while maintaining the old behaviour.
//
// TODO: Remove this fork and update existing logic/tests.
func parseExports(inp []string) ([]*controllerapi.ExportEntry, error) {
if len(inp) == 0 {
return nil, nil
}
outs := make([]*controllerapi.ExportEntry, 0, len(inp))
for _, s := range inp {
fields, err := csvvalue.Fields(s, nil)
if err != nil {
return nil, err
}
out := controllerapi.ExportEntry{
Attrs: map[string]string{},
}
if len(fields) == 1 && fields[0] == s && !strings.HasPrefix(s, "type=") {
if s != "-" {
outs = append(outs, &controllerapi.ExportEntry{
Type: client.ExporterLocal,
Destination: s,
})
continue
}
out = controllerapi.ExportEntry{
Type: client.ExporterTar,
Destination: s,
}
}
if out.Type == "" {
for _, field := range fields {
parts := strings.SplitN(field, "=", 2)
if len(parts) != 2 {
return nil, fmt.Errorf("invalid value %s", field)
}
key := strings.TrimSpace(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.New("type is required for output")
}
if out.Type == "registry" {
out.Type = client.ExporterImage
if _, ok := out.Attrs["push"]; !ok {
out.Attrs["push"] = "true"
}
}
if dest, ok := out.Attrs["dest"]; ok {
out.Destination = dest
delete(out.Attrs, "dest")
}
outs = append(outs, &out)
}
return outs, nil
}
func (e Export) validate(preview bool, tags []string) (*controllerapi.ExportEntry, error) {
if strings.Count(e.String(), "type=") > 1 {
return nil, errors.New("exports should only specify one export type")
}
ee, err := buildflags.ParseExports([]string{e.String()})
ee, err := parseExports([]string{e.String()})
if err != nil {
return nil, err
}
exp := ee[0]
if len(tags) == 0 && isRegistryPush(exp) && exp.Attrs["name"] == "" {
return nil, errors.New(