fix(packages): return empty Chef dependencies instead of null (#40237) (#40242)

Backport #40237 by @Shadow0Bit

Fixes #40230

Cookbooks without dependencies are served with `"dependencies": null`
from the universe and version metadata endpoints. This happens because
`omitempty` drops the empty map when the metadata is stored. Chef
clients iterate over this value and Policyfile resolution crashes.

Supermarket returns `{}` for these cookbooks, Gitea now does too. The
fix checks whether dependencies are `nil` while building response,
returns an empty map if true. This way cookbooks that are already stored
are covered without a re-upload.

Tested with the Chef integration test and `chef-cli install`.

Assisted by Claude Code (claude-opus-5-5).

Co-authored-by: rafał przypek <58443512+Shadow0Bit@users.noreply.github.com>
This commit is contained in:
Giteabot
2026-11-05 19:03:52 +00:00
committed by GitHub
parent b9a99f0f3e
commit d0e8294f46
3 changed files with 11 additions and 2 deletions

View File

@@ -77,3 +77,10 @@ func SliceNilAsEmpty[T any](a []T) []T {
}
return a
}
func MapNilAsEmpty[K comparable, V any](m map[K]V) map[K]V {
if m == nil {
return map[K]V{}
}
return m
}

View File

@@ -71,7 +71,7 @@ func PackagesUniverse(ctx *context.Context) {
LocationType: "opscode",
LocationPath: baseURL,
DownloadURL: fmt.Sprintf("%s/cookbooks/%s/versions/%s/download", baseURL, url.PathEscape(pd.Package.Name), pd.Version.Version),
Dependencies: packages_model.DescriptorMetadata[*chef_module.Metadata](pd).Dependencies,
Dependencies: util.MapNilAsEmpty(packages_model.DescriptorMetadata[*chef_module.Metadata](pd).Dependencies),
}
}
@@ -250,7 +250,7 @@ func PackageVersionMetadata(ctx *context.Context) {
Cookbook: baseURL,
File: fmt.Sprintf("%s/versions/%s/download", baseURL, pd.Version.Version),
License: metadata.License,
Dependencies: metadata.Dependencies,
Dependencies: util.MapNilAsEmpty(metadata.Dependencies),
})
}

View File

@@ -360,6 +360,7 @@ nwIDAQAB
assert.Equal(t, "opscode", info.LocationType)
assert.Equal(t, setting.AppURL+root[1:], info.LocationPath)
assert.Equal(t, fmt.Sprintf("%s%s/cookbooks/%s/versions/%s/download", setting.AppURL, root[1:], packageName, packageVersion), info.DownloadURL)
assert.Equal(t, map[string]string{}, info.Dependencies)
})
t.Run("Search", func(t *testing.T) {
@@ -516,6 +517,7 @@ nwIDAQAB
assert.Equal(t, packageVersion, result.Version)
assert.Equal(t, packageURL, result.Cookbook)
assert.Equal(t, fmt.Sprintf("%s/versions/%s/download", packageURL, packageVersion), result.File)
assert.Equal(t, map[string]string{}, result.Dependencies)
})
t.Run("Delete", func(t *testing.T) {