From d0e8294f468a7b77c8166c65cc5e0022a34b73c3 Mon Sep 17 00:00:00 2001 From: Giteabot Date: Thu, 5 Nov 2026 19:03:52 +0000 Subject: [PATCH] fix(packages): return empty Chef dependencies instead of null (#40237) (#40242) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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> --- modules/util/slice.go | 7 +++++++ routers/api/packages/chef/chef.go | 4 ++-- tests/integration/api_packages_chef_test.go | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/modules/util/slice.go b/modules/util/slice.go index aaa729c1c9b..27df4de3b44 100644 --- a/modules/util/slice.go +++ b/modules/util/slice.go @@ -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 +} diff --git a/routers/api/packages/chef/chef.go b/routers/api/packages/chef/chef.go index ae4a874de12..1a797af1584 100644 --- a/routers/api/packages/chef/chef.go +++ b/routers/api/packages/chef/chef.go @@ -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), }) } diff --git a/tests/integration/api_packages_chef_test.go b/tests/integration/api_packages_chef_test.go index f179baac3de..343e4ea34c0 100644 --- a/tests/integration/api_packages_chef_test.go +++ b/tests/integration/api_packages_chef_test.go @@ -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) {