Skip to content

Commit 92eb76d

Browse files
authored
refactor: moves injection to project loader and improves injection code (#70)
1 parent 9617c01 commit 92eb76d

30 files changed

+1410
-896
lines changed

cli/blueprint.cue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ project: {
44
ci: targets: {
55
publish: {
66
args: {
7-
version: string | *"dev" @env(name="GIT_TAG",type="string")
7+
version: string | *"dev" @forge(name="GIT_TAG")
88
}
99
platforms: [
1010
"linux/amd64",
@@ -13,7 +13,7 @@ project: {
1313
}
1414
release: {
1515
args: {
16-
version: string | *"dev" @env(name="GIT_TAG",type="string")
16+
version: string | *"dev" @forge(name="GIT_TAG")
1717
}
1818
platforms: [
1919
"linux/amd64",

foundry/api/blueprint.cue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ project: {
44
ci: targets: {
55
publish: {
66
args: {
7-
version: string | *"dev" @env(name="GIT_TAG",type="string")
7+
version: string | *"dev" @forge(name="GIT_TAG")
88
}
99
}
1010
release: {
1111
args: {
12-
version: string | *"dev" @env(name="GIT_TAG",type="string")
12+
version: string | *"dev" @forge(name="GIT_TAG")
1313
}
1414
}
1515
}
@@ -21,7 +21,7 @@ project: {
2121
values: {
2222
environment: name: "dev"
2323
server: image: {
24-
tag: _ @env(name="GIT_IMAGE_TAG",type="string")
24+
tag: _ @forge(name="GIT_IMAGE_TAG")
2525
}
2626
}
2727
}

lib/project/blueprint/blueprint.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66

77
"cuelang.org/go/cue"
88
"github.com/Masterminds/semver/v3"
9-
"github.com/input-output-hk/catalyst-forge/lib/project/injector"
109
"github.com/input-output-hk/catalyst-forge/lib/project/version"
1110
cuetools "github.com/input-output-hk/catalyst-forge/lib/tools/cue"
1211
)
@@ -75,7 +74,7 @@ func (b BlueprintFiles) Version() *semver.Version {
7574
// injecting any necessary environment variables. Additionally, the version is
7675
// extracted from the CUE value. If the version is not found or invalid, or the
7776
// final CUE value is invalid, an error is returned.
78-
func NewBlueprintFile(ctx *cue.Context, path string, contents []byte, inj injector.Injector) (BlueprintFile, error) {
77+
func NewBlueprintFile(ctx *cue.Context, path string, contents []byte) (BlueprintFile, error) {
7978
v, err := cuetools.Compile(ctx, contents)
8079
if err != nil {
8180
return BlueprintFile{}, fmt.Errorf("failed to compile CUE file: %w", err)

lib/project/blueprint/loader.go

Lines changed: 14 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,11 @@ import (
88
"path/filepath"
99

1010
"cuelang.org/go/cue"
11-
"cuelang.org/go/cue/cuecontext"
1211
"cuelang.org/go/cue/errors"
1312
"github.com/Masterminds/semver/v3"
1413
"github.com/input-output-hk/catalyst-forge/lib/project/blueprint/defaults"
15-
"github.com/input-output-hk/catalyst-forge/lib/project/injector"
1614
"github.com/input-output-hk/catalyst-forge/lib/project/schema"
1715
"github.com/input-output-hk/catalyst-forge/lib/project/version"
18-
cuetools "github.com/input-output-hk/catalyst-forge/lib/tools/cue"
1916
"github.com/spf13/afero"
2017
)
2118

@@ -27,25 +24,17 @@ var (
2724
ErrVersionNotFound = errors.New("version not found")
2825
)
2926

30-
// InjectorOverrider is a function that receives a CUE value and returns a map
31-
// of environment variables to override
32-
type InjectorOverrider func(value cue.Value) map[string]string
33-
3427
// BlueprintLoader is an interface for loading blueprints.
3528
type BlueprintLoader interface {
3629
// Load loads the blueprint.
3730
Load(projectPath, gitRootPath string) (RawBlueprint, error)
38-
39-
// SetOverrider sets the InjectorOverrider.
40-
SetOverrider(overrider InjectorOverrider)
4131
}
4232

4333
// DefaultBlueprintLoader is the default implementation of the BlueprintLoader
4434
type DefaultBlueprintLoader struct {
45-
fs afero.Fs
46-
injector injector.Injector
47-
logger *slog.Logger
48-
overrider InjectorOverrider
35+
ctx *cue.Context
36+
fs afero.Fs
37+
logger *slog.Logger
4938
}
5039

5140
func (b *DefaultBlueprintLoader) Load(projectPath, gitRootPath string) (RawBlueprint, error) {
@@ -79,8 +68,7 @@ func (b *DefaultBlueprintLoader) Load(projectPath, gitRootPath string) (RawBluep
7968
}
8069
}
8170

82-
ctx := cuecontext.New()
83-
schema, err := schema.LoadSchema(ctx)
71+
schema, err := schema.LoadSchema(b.ctx)
8472
if err != nil {
8573
b.logger.Error("Failed to load schema", "error", err)
8674
return RawBlueprint{}, fmt.Errorf("failed to load schema: %w", err)
@@ -92,7 +80,7 @@ func (b *DefaultBlueprintLoader) Load(projectPath, gitRootPath string) (RawBluep
9280
if len(files) > 0 {
9381
for path, data := range files {
9482
b.logger.Info("Loading blueprint file", "path", path)
95-
bp, err := NewBlueprintFile(ctx, path, data, b.injector)
83+
bp, err := NewBlueprintFile(b.ctx, path, data)
9684
if err != nil {
9785
b.logger.Error("Failed to load blueprint file", "path", path, "error", err)
9886
return RawBlueprint{}, fmt.Errorf("failed to load blueprint file: %w", err)
@@ -106,21 +94,14 @@ func (b *DefaultBlueprintLoader) Load(projectPath, gitRootPath string) (RawBluep
10694
return RawBlueprint{}, err
10795
}
10896

109-
userBlueprint, err := bps.Unify(ctx)
97+
userBlueprint, err := bps.Unify(b.ctx)
11098
if err != nil {
11199
b.logger.Error("Failed to unify blueprint files", "error", err)
112100
return RawBlueprint{}, fmt.Errorf("failed to unify blueprint files: %w", err)
113101
}
114102

115103
finalVersion = bps.Version()
116104
userBlueprint = userBlueprint.FillPath(cue.ParsePath("version"), finalVersion.String())
117-
118-
var overrides map[string]string
119-
if b.overrider != nil {
120-
overrides = b.overrider(userBlueprint)
121-
}
122-
123-
userBlueprint = b.injector.InjectEnv(userBlueprint, overrides)
124105
finalBlueprint = schema.Unify(userBlueprint)
125106
} else {
126107
b.logger.Warn("No blueprint files found, using default values")
@@ -138,11 +119,6 @@ func (b *DefaultBlueprintLoader) Load(projectPath, gitRootPath string) (RawBluep
138119
}
139120
}
140121

141-
if err := cuetools.Validate(finalBlueprint, cue.Concrete(true)); err != nil {
142-
b.logger.Error("Failed to validate full blueprint", "error", err)
143-
return RawBlueprint{}, err
144-
}
145-
146122
if err := version.ValidateVersions(finalVersion, schema.Version); err != nil {
147123
if errors.Is(err, version.ErrMinorMismatch) {
148124
b.logger.Warn("The minor version of the blueprint is greater than the supported version", "version", finalVersion)
@@ -155,41 +131,33 @@ func (b *DefaultBlueprintLoader) Load(projectPath, gitRootPath string) (RawBluep
155131
return NewRawBlueprint(finalBlueprint), nil
156132
}
157133

158-
// SetOverrider sets the InjectorOverrider.
159-
func (b *DefaultBlueprintLoader) SetOverrider(overrider InjectorOverrider) {
160-
b.overrider = overrider
161-
}
162-
163134
// NewDefaultBlueprintLoader creates a new DefaultBlueprintLoader.
164-
func NewDefaultBlueprintLoader(overrider InjectorOverrider, logger *slog.Logger) DefaultBlueprintLoader {
135+
func NewDefaultBlueprintLoader(ctx *cue.Context, logger *slog.Logger) DefaultBlueprintLoader {
165136
if logger == nil {
166137
logger = slog.New(slog.NewTextHandler(io.Discard, nil))
167138
}
168139

169140
return DefaultBlueprintLoader{
170-
fs: afero.NewOsFs(),
171-
injector: injector.NewDefaultInjector(logger),
172-
logger: logger,
173-
overrider: overrider,
141+
ctx: ctx,
142+
fs: afero.NewOsFs(),
143+
logger: logger,
174144
}
175145
}
176146

177147
// NewCustomBlueprintLoader creates a new DefaultBlueprintLoader with custom
178148
// dependencies.
179149
func NewCustomBlueprintLoader(
150+
ctx *cue.Context,
180151
fs afero.Fs,
181-
injector injector.Injector,
182-
overrider InjectorOverrider,
183152
logger *slog.Logger,
184153
) DefaultBlueprintLoader {
185154
if logger == nil {
186155
logger = slog.New(slog.NewTextHandler(io.Discard, nil))
187156
}
188157

189158
return DefaultBlueprintLoader{
190-
fs: fs,
191-
injector: injector,
192-
logger: logger,
193-
overrider: overrider,
159+
ctx: ctx,
160+
fs: fs,
161+
logger: logger,
194162
}
195163
}

lib/project/blueprint/loader_test.go

Lines changed: 17 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ import (
88
"testing"
99

1010
"cuelang.org/go/cue"
11-
"github.com/input-output-hk/catalyst-forge/lib/project/injector"
12-
imocks "github.com/input-output-hk/catalyst-forge/lib/project/injector/mocks"
11+
"cuelang.org/go/cue/cuecontext"
1312
"github.com/input-output-hk/catalyst-forge/lib/tools/testutils"
1413
"github.com/spf13/afero"
1514
"github.com/stretchr/testify/assert"
@@ -44,49 +43,32 @@ func NewMockFileSeeker(s string) MockFileSeeker {
4443
}
4544

4645
func TestBlueprintLoaderLoad(t *testing.T) {
47-
defaultInjector := func() injector.Injector {
48-
return injector.NewInjector(
49-
slog.New(slog.NewTextHandler(io.Discard, nil)),
50-
&imocks.EnvGetterMock{
51-
GetFunc: func(name string) (string, bool) {
52-
return "", false
53-
},
54-
},
55-
)
56-
}
57-
5846
tests := []struct {
5947
name string
6048
fs afero.Fs
61-
injector injector.Injector
62-
overrider InjectorOverrider
6349
project string
6450
gitRoot string
6551
files map[string]string
6652
cond func(*testing.T, cue.Value)
6753
expectErr bool
6854
}{
6955
{
70-
name: "no files",
71-
fs: afero.NewMemMapFs(),
72-
injector: defaultInjector(),
73-
overrider: nil,
74-
project: "/tmp/dir1/dir2",
75-
gitRoot: "/tmp/dir1/dir2",
76-
files: map[string]string{},
56+
name: "no files",
57+
fs: afero.NewMemMapFs(),
58+
project: "/tmp/dir1/dir2",
59+
gitRoot: "/tmp/dir1/dir2",
60+
files: map[string]string{},
7761
cond: func(t *testing.T, v cue.Value) {
7862
assert.NoError(t, v.Err())
7963
assert.NotEmpty(t, v.LookupPath(cue.ParsePath("version")))
8064
},
8165
expectErr: false,
8266
},
8367
{
84-
name: "single file",
85-
fs: afero.NewMemMapFs(),
86-
injector: defaultInjector(),
87-
overrider: nil,
88-
project: "/tmp/dir1/dir2",
89-
gitRoot: "/tmp/dir1/dir2",
68+
name: "single file",
69+
fs: afero.NewMemMapFs(),
70+
project: "/tmp/dir1/dir2",
71+
gitRoot: "/tmp/dir1/dir2",
9072
files: map[string]string{
9173
"/tmp/dir1/dir2/blueprint.cue": `
9274
version: "1.0"
@@ -113,12 +95,10 @@ func TestBlueprintLoaderLoad(t *testing.T) {
11395
expectErr: false,
11496
},
11597
{
116-
name: "multiple files",
117-
fs: afero.NewMemMapFs(),
118-
injector: defaultInjector(),
119-
overrider: nil,
120-
project: "/tmp/dir1/dir2",
121-
gitRoot: "/tmp/dir1",
98+
name: "multiple files",
99+
fs: afero.NewMemMapFs(),
100+
project: "/tmp/dir1/dir2",
101+
gitRoot: "/tmp/dir1",
122102
files: map[string]string{
123103
"/tmp/dir1/dir2/blueprint.cue": `
124104
version: "1.0"
@@ -157,96 +137,16 @@ func TestBlueprintLoaderLoad(t *testing.T) {
157137
},
158138
expectErr: false,
159139
},
160-
{
161-
name: "with injection",
162-
fs: afero.NewMemMapFs(),
163-
injector: injector.NewInjector(
164-
slog.New(slog.NewTextHandler(io.Discard, nil)),
165-
&imocks.EnvGetterMock{
166-
GetFunc: func(name string) (string, bool) {
167-
if name == "RETRIES" {
168-
return "5", true
169-
}
170-
171-
return "", false
172-
},
173-
},
174-
),
175-
overrider: nil,
176-
project: "/tmp/dir1/dir2",
177-
gitRoot: "/tmp/dir1/dir2",
178-
files: map[string]string{
179-
"/tmp/dir1/dir2/blueprint.cue": `
180-
version: "1.0"
181-
project: {
182-
name: "test"
183-
ci: {
184-
targets: {
185-
test: {
186-
retries: _ @env(name=RETRIES,type=int)
187-
}
188-
}
189-
}
190-
}
191-
`,
192-
"/tmp/dir1/.git": "",
193-
},
194-
cond: func(t *testing.T, v cue.Value) {
195-
assert.NoError(t, v.Err())
196-
197-
field, err := v.LookupPath(cue.ParsePath("project.ci.targets.test.retries")).Int64()
198-
require.NoError(t, err)
199-
assert.Equal(t, int64(5), field)
200-
},
201-
expectErr: false,
202-
},
203-
{
204-
name: "with injection overrides",
205-
fs: afero.NewMemMapFs(),
206-
injector: defaultInjector(),
207-
overrider: func(bp cue.Value) map[string]string {
208-
return map[string]string{
209-
"RETRIES": "5",
210-
}
211-
},
212-
project: "/tmp/dir1/dir2",
213-
gitRoot: "/tmp/dir1/dir2",
214-
files: map[string]string{
215-
"/tmp/dir1/dir2/blueprint.cue": `
216-
version: "1.0"
217-
project: {
218-
name: "test"
219-
ci: {
220-
targets: {
221-
test: {
222-
retries: _ @env(name=RETRIES,type=int)
223-
}
224-
}
225-
}
226-
}
227-
`,
228-
"/tmp/dir1/.git": "",
229-
},
230-
cond: func(t *testing.T, v cue.Value) {
231-
assert.NoError(t, v.Err())
232-
233-
field, err := v.LookupPath(cue.ParsePath("project.ci.targets.test.retries")).Int64()
234-
require.NoError(t, err)
235-
assert.Equal(t, int64(5), field)
236-
},
237-
expectErr: false,
238-
},
239140
}
240141

241142
for _, tt := range tests {
242143
t.Run(tt.name, func(t *testing.T) {
243144
testutils.SetupFS(t, tt.fs, tt.files)
244145

245146
loader := DefaultBlueprintLoader{
246-
fs: tt.fs,
247-
injector: tt.injector,
248-
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
249-
overrider: tt.overrider,
147+
ctx: cuecontext.New(),
148+
fs: tt.fs,
149+
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
250150
}
251151

252152
bp, err := loader.Load(tt.project, tt.gitRoot)

0 commit comments

Comments
 (0)