Skip to content

Commit 447e6fc

Browse files
committed
add support for dependencies manifest
Signed-off-by: Pablo Chacin <pablochacin@gmail.com>
1 parent 3160bde commit 447e6fc

File tree

5 files changed

+133
-1
lines changed

5 files changed

+133
-1
lines changed

cmd/state/state.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ const (
2828
// AutoExtensionResolution defines the environment variable that enables using extensions natively
2929
AutoExtensionResolution = "K6_AUTO_EXTENSION_RESOLUTION"
3030

31+
// DependenciesManifest defines the default values for dependency resolution
32+
DependenciesManifest = "K6_DEPENDENCIES_MANIFEST"
33+
3134
// communityExtensionsCatalog defines the catalog for community extensions
3235
communityExtensionsCatalog = "oss"
3336

@@ -185,6 +188,7 @@ type GlobalFlags struct {
185188
BuildServiceURL string
186189
BinaryCache string
187190
EnableCommunityExtensions bool
191+
DependenciesManifest string
188192
}
189193

190194
// GetDefaultFlags returns the default global flags.
@@ -250,6 +254,9 @@ func getFlags(defaultFlags GlobalFlags, env map[string]string, args []string) Gl
250254
result.EnableCommunityExtensions = vb
251255
}
252256
}
257+
if val, ok := env["K6_DEPENDENCIES_MANIFEST"]; ok {
258+
result.DependenciesManifest = val
259+
}
253260

254261
// adjust BuildServiceURL if community extensions are enable
255262
// community extensions flag only takes effect if the default build service is used

internal/cmd/deps.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ func getCmdDeps(gs *state.GlobalState) *cobra.Command {
3838
}
3939
depsMap[name] = constraint.String()
4040
}
41+
42+
depsMap, err = mergeManifest(depsMap, gs.Flags.DependenciesManifest)
43+
if err != nil {
44+
return err
45+
}
46+
4147
imports := test.Imports()
4248
slices.Sort(imports)
4349

@@ -69,3 +75,5 @@ func getCmdDeps(gs *state.GlobalState) *cobra.Command {
6975

7076
return depsCmd
7177
}
78+
79+

internal/cmd/launcher.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cmd
22

33
import (
44
"bytes"
5+
"encoding/json"
56
"errors"
67
"fmt"
78
"os"
@@ -299,3 +300,32 @@ func findDirectives(text []byte) []string {
299300
}
300301
return result
301302
}
303+
304+
func mergeManifest(deps map[string]string, manifestString string) (map[string]string, error) {
305+
if manifestString == "" {
306+
return deps, nil
307+
}
308+
309+
manifest := make(map[string]string)
310+
if err := json.Unmarshal([]byte(manifestString), &manifest); err != nil {
311+
return nil, fmt.Errorf("invalid dependencies manifest %w", err)
312+
}
313+
314+
result := make(map[string]string)
315+
for dep, constraint := range deps {
316+
result[dep] = constraint
317+
318+
// if deps has a non default constrain, keep ip
319+
if constraint != "" && constraint != "*" {
320+
continue
321+
}
322+
323+
// check if there's an override in the manifest
324+
manifestConstrain := manifest[dep]
325+
if manifestConstrain != "" && manifestConstrain != "*" {
326+
result[dep] = manifestConstrain
327+
}
328+
}
329+
330+
return result, nil
331+
}

internal/cmd/launcher_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,3 +314,77 @@ const l = 5
314314
})
315315
}
316316
}
317+
318+
func TestMergeManifest(t *testing.T) {
319+
t.Parallel()
320+
321+
tests := []struct {
322+
name string
323+
deps map[string]string
324+
manifest string
325+
expected map[string]string
326+
expectedError string
327+
}{
328+
{
329+
name: "default constrain with no manifest",
330+
deps: map[string]string{"k6/x/dep": "*"},
331+
manifest: "",
332+
expected: map[string]string{"k6/x/dep": "*"},
333+
},
334+
{
335+
name: "empty constrain with no manifest",
336+
deps: map[string]string{"k6/x/dep": ""},
337+
manifest: "",
338+
expected: map[string]string{"k6/x/dep": ""},
339+
},
340+
{
341+
name: "default constrain with empty manifest",
342+
deps: map[string]string{"k6/x/dep": "*"},
343+
manifest: "{}",
344+
expected: map[string]string{"k6/x/dep": "*"},
345+
},
346+
{
347+
name: "default constrain with manifest overrides",
348+
deps: map[string]string{"k6/x/dep": "*"},
349+
manifest: `{"k6/x/dep": "=v0.0.0"}`,
350+
expected: map[string]string{"k6/x/dep": "=v0.0.0"},
351+
},
352+
{
353+
name: "dependency with version constraint",
354+
deps: map[string]string{"k6/x/dep": "=v0.0.1"},
355+
manifest: `{"k6/x/dep": "=v0.0.0"}`,
356+
expected: map[string]string{"k6/x/dep": "=v0.0.1"},
357+
},
358+
{
359+
name: "manifest with different dependency",
360+
deps: map[string]string{"k6/x/dep": "*"},
361+
manifest: `{"k6/x/another": "=v0.0.0"}`,
362+
expected: map[string]string{"k6/x/dep": "*"},
363+
},
364+
{
365+
name: "no dependencies",
366+
deps: map[string]string{},
367+
manifest: `{"k6/x/dep": "=v0.0.0"}`,
368+
expected: map[string]string{},
369+
},
370+
{
371+
name: "malformed manifest",
372+
deps: map[string]string{"k6/x/dep": "*"},
373+
manifest: `{"k6/x/dep": }`,
374+
expectedError: "invalid dependencies manifest",
375+
},
376+
}
377+
for _, test := range tests {
378+
t.Run(test.name, func(t *testing.T) {
379+
t.Parallel()
380+
381+
merged, err := mergeManifest(test.deps, test.manifest)
382+
383+
if len(test.expectedError) > 0 {
384+
require.ErrorContains(t, err, test.expectedError)
385+
} else {
386+
require.EqualValues(t, test.expected, merged)
387+
}
388+
})
389+
}
390+
}

internal/cmd/root.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,20 @@ func handleUnsatisfiedDependencies(err error, c *rootCommand) (exitcodes.ExitCod
166166
" it's required to provision a custom binary.")
167167
provisioner := newK6BuildProvisioner(c.globalState)
168168
var customBinary commandExecutor
169-
customBinary, err = provisioner.provision(constraintsMapToProvisionDependency(deps))
169+
170+
// merge the script dependencies with the dependencies manifest, if specified
171+
manifest := c.globalState.Flags.DependenciesManifest
172+
depsMap, err := mergeManifest(constraintsMapToProvisionDependency(deps), manifest)
173+
if err != nil {
174+
c.globalState.Logger.
175+
WithField("deps", deps).
176+
WithField("manifest", manifest).
177+
WithError(err).
178+
Error("Failed to process the dependecies manifest for automatic dependency resolution")
179+
return 0, err
180+
}
181+
182+
customBinary, err = provisioner.provision(depsMap)
170183
if err != nil {
171184
err = errext.WithExitCodeIfNone(err, exitcodes.ScriptException)
172185
c.globalState.Logger.

0 commit comments

Comments
 (0)