From f8a13e167448c8b79b49ee3ae852cc97209a3c44 Mon Sep 17 00:00:00 2001 From: Jordan Carter Date: Wed, 17 Apr 2024 10:45:57 +1200 Subject: [PATCH 1/2] Add Name and Size fields to Cache. Add omitempty to Cache.Paths. Teach Cache to use inlineFriendlyMarshalJSON so that RemainingFields is inlined. --- step_command_cache.go | 16 +++++++-- step_command_cache_test.go | 68 +++++++++++++++++++++++++++++++++++++- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/step_command_cache.go b/step_command_cache.go index 3dc1111..35a88a6 100644 --- a/step_command_cache.go +++ b/step_command_cache.go @@ -1,12 +1,16 @@ package pipeline import ( + "encoding/json" "fmt" "github.com/buildkite/go-pipeline/ordered" ) -var _ ordered.Unmarshaler = (*Cache)(nil) +var _ interface { + json.Marshaler + ordered.Unmarshaler +} = (*Cache)(nil) var ( errUnsupportedCacheType = fmt.Errorf("unsupported type for cache") @@ -14,11 +18,19 @@ var ( // Cache models the cache settings for a given step type Cache struct { - Paths []string `json:"paths" yaml:"paths"` + Name string `yaml:"name,omitempty"` + Paths []string `yaml:"paths,omitempty"` + Size string `yaml:"size,omitempty"` RemainingFields map[string]any `yaml:",inline"` } +// MarshalJSON marshals the step to JSON. Special handling is needed because +// yaml.v3 has "inline" but encoding/json has no concept of it. +func (c *Cache) MarshalJSON() ([]byte, error) { + return inlineFriendlyMarshalJSON(c) +} + // UnmarshalOrdered unmarshals from the following types: // - string: a single path // - []string: multiple paths diff --git a/step_command_cache_test.go b/step_command_cache_test.go index 9fe4222..57b01db 100644 --- a/step_command_cache_test.go +++ b/step_command_cache_test.go @@ -8,6 +8,66 @@ import ( "github.com/google/go-cmp/cmp" ) +func TestCacheMarshalJSON(t *testing.T) { + t.Parallel() + + cases := []struct { + name string + c Cache + want string + }{ + { + name: "single path", + c: Cache{Paths: []string{"path/to/cache"}}, + want: `{"paths":["path/to/cache"]}`, + }, + { + name: "multiple paths", + c: Cache{Paths: []string{"path/to/cache", "another/path"}}, + want: `{"paths":["path/to/cache","another/path"]}`, + }, + { + name: "empty cache settings block", + c: Cache{}, + want: `{}`, + }, + { + name: "full cache settings block", + c: Cache{ + Paths: []string{"path/to/cache", "another/path"}, + Name: "cache-name", + Size: "25g", + }, + want: `{"name":"cache-name","paths":["path/to/cache","another/path"],"size":"25g"}`, + }, + { + name: "full cache settings block with extra fields", + c: Cache{ + Paths: []string{"path/to/cache", "another/path"}, + Name: "cache-name", + Size: "25g", + RemainingFields: map[string]any{"extra": "field"}, + }, + want: `{"extra":"field","name":"cache-name","paths":["path/to/cache","another/path"],"size":"25g"}`, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + b, err := tc.c.MarshalJSON() + if err != nil { + t.Fatalf("Cache.MarshalJSON() error: %v", err) + } + + if diff := cmp.Diff(string(b), tc.want); diff != "" { + t.Errorf("Cache.MarshalJSON() diff (-got +want):\n%s", diff) + } + }) + } +} + func TestCacheUnmarshalOrdered(t *testing.T) { t.Parallel() @@ -31,17 +91,23 @@ func TestCacheUnmarshalOrdered(t *testing.T) { name: "full cache settings block", input: ordered.MapFromItems( ordered.TupleSA{Key: "paths", Value: []any{"path/to/cache", "another/path"}}, + ordered.TupleSA{Key: "name", Value: "cache-name"}, + ordered.TupleSA{Key: "size", Value: "25g"}, ), - want: Cache{Paths: []string{"path/to/cache", "another/path"}}, + want: Cache{Paths: []string{"path/to/cache", "another/path"}, Name: "cache-name", Size: "25g"}, }, { name: "full cache settings block with extra fields", input: ordered.MapFromItems( ordered.TupleSA{Key: "paths", Value: []any{"path/to/cache", "another/path"}}, + ordered.TupleSA{Key: "name", Value: "cache-name"}, + ordered.TupleSA{Key: "size", Value: "25g"}, ordered.TupleSA{Key: "extra", Value: "field"}, ), want: Cache{ Paths: []string{"path/to/cache", "another/path"}, + Name: "cache-name", + Size: "25g", RemainingFields: map[string]any{"extra": "field"}, }, }, From e3bb1c43c275ab1d170898bf9b0080eccafeda4c Mon Sep 17 00:00:00 2001 From: Jordan Carter Date: Wed, 17 Apr 2024 15:02:18 +1200 Subject: [PATCH 2/2] Add support for cache: false --- step_command_cache.go | 14 +++++++++++--- step_command_cache_test.go | 5 +++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/step_command_cache.go b/step_command_cache.go index 35a88a6..6427bb3 100644 --- a/step_command_cache.go +++ b/step_command_cache.go @@ -18,9 +18,10 @@ var ( // Cache models the cache settings for a given step type Cache struct { - Name string `yaml:"name,omitempty"` - Paths []string `yaml:"paths,omitempty"` - Size string `yaml:"size,omitempty"` + Disabled bool `yaml:",omitempty"` + Name string `yaml:"name,omitempty"` + Paths []string `yaml:"paths,omitempty"` + Size string `yaml:"size,omitempty"` RemainingFields map[string]any `yaml:",inline"` } @@ -28,6 +29,9 @@ type Cache struct { // MarshalJSON marshals the step to JSON. Special handling is needed because // yaml.v3 has "inline" but encoding/json has no concept of it. func (c *Cache) MarshalJSON() ([]byte, error) { + if c.Disabled { + return json.Marshal(false) + } return inlineFriendlyMarshalJSON(c) } @@ -37,6 +41,10 @@ func (c *Cache) MarshalJSON() ([]byte, error) { // - ordered.Map: a map containing paths, among potentially other things func (c *Cache) UnmarshalOrdered(o any) error { switch v := o.(type) { + case bool: + if !v { + c.Disabled = true + } case string: c.Paths = []string{v} diff --git a/step_command_cache_test.go b/step_command_cache_test.go index 57b01db..3a86bdf 100644 --- a/step_command_cache_test.go +++ b/step_command_cache_test.go @@ -50,6 +50,11 @@ func TestCacheMarshalJSON(t *testing.T) { }, want: `{"extra":"field","name":"cache-name","paths":["path/to/cache","another/path"],"size":"25g"}`, }, + { + name: "disabled cache", + c: Cache{Disabled: true}, + want: `false`, + }, } for _, tc := range cases {