-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcache.go
More file actions
119 lines (107 loc) · 3.43 KB
/
cache.go
File metadata and controls
119 lines (107 loc) · 3.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package zstash
import (
"fmt"
"runtime"
"github.com/buildkite/zstash/cache"
"github.com/buildkite/zstash/configuration"
)
// NewCache creates and validates a new cache client.
//
// The function performs the following steps:
// 1. Validates the configuration (Client must be provided)
// 2. Sets defaults for Format (zip) and Platform (runtime.GOOS/runtime.GOARCH)
// 3. Expands cache templates using cfg.Env if provided, otherwise uses OS environment
// 4. Validates all expanded cache configurations
// 5. Returns a ready-to-use cache client
//
// The returned Cache client is safe for concurrent use by multiple goroutines.
//
// Returns ErrInvalidConfiguration (wrapped) if:
// - Template expansion fails
// - Cache validation fails (invalid paths, missing required fields, etc.)
//
// Example:
//
// client := api.NewClient(ctx, version, endpoint, token)
// cacheClient, err := zstash.NewCache(zstash.Config{
// Client: client,
// BucketURL: "s3://my-bucket",
// Branch: "main",
// Pipeline: "my-pipeline",
// Caches: []cache.Cache{
// {ID: "deps", Key: "v1-{{ checksum \"go.mod\" }}", Paths: []string{"vendor"}},
// },
// })
// if err != nil {
// log.Fatal(err)
// }
func NewCache(cfg Config) (*Cache, error) {
// Validate required configuration
// Note: Client is a struct, so we can't check for nil. It should be created via NewClient.
// We trust that if passed, it was properly initialized.
// Set defaults
if cfg.Format == "" {
cfg.Format = "zip"
}
if cfg.Platform == "" {
cfg.Platform = fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
}
if cfg.Registry == "" {
cfg.Registry = "~"
}
var (
err error
// Expand cache configurations
expandedCaches []cache.Cache
)
if cfg.Env != nil {
// If environment is provided, expand cache templates
expandedCaches, err = configuration.ExpandCacheConfigurationWithEnv(cfg.Caches, cfg.Env)
if err != nil {
return nil, fmt.Errorf("%w: failed to expand cache configuration: %w", ErrInvalidConfiguration, err)
}
} else {
// Use OS environment for expansion
expandedCaches, err = configuration.ExpandCacheConfiguration(cfg.Caches)
if err != nil {
return nil, fmt.Errorf("%w: failed to expand cache configuration: %w", ErrInvalidConfiguration, err)
}
}
// Validate all caches
for _, c := range expandedCaches {
if err := c.Validate(); err != nil {
return nil, fmt.Errorf("%w: cache validation failed for ID %s: %w", ErrInvalidConfiguration, c.ID, err)
}
}
return &Cache{
client: cfg.Client,
bucketURL: cfg.BucketURL,
format: cfg.Format,
branch: cfg.Branch,
pipeline: cfg.Pipeline,
organization: cfg.Organization,
platform: cfg.Platform,
registry: cfg.Registry,
caches: expandedCaches,
onProgress: cfg.OnProgress,
}, nil
}
// callProgress safely calls the progress callback if it exists
func (c *Cache) callProgress(cacheID string, stage string, message string, current int, total int) {
if c.onProgress != nil {
// Protect against panics in user-provided callback
defer func() {
_ = recover() // Ignore panics - user callbacks shouldn't break the cache client
}()
c.onProgress(cacheID, stage, message, current, total)
}
}
// findCache finds a cache by ID in the cache client's cache list
func (c *Cache) findCache(id string) (*cache.Cache, error) {
for i := range c.caches {
if c.caches[i].ID == id {
return &c.caches[i], nil
}
}
return nil, ErrCacheNotFound
}