-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Workflow bundling for runtime #4
Changes from 7 commits
5735492
3f564b4
50a4181
9cdfb8c
04fbe28
c1c9572
83a0118
b86fc17
900fc48
abc4b39
4a5ae01
aeff3ce
f3e0344
c544d0f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -23,8 +23,44 @@ type ( | |||||||||
Context map[string]interface{} `json:"context"` | ||||||||||
ExitResult interface{} `json:"exit_result"` | ||||||||||
} | ||||||||||
introspectedExport struct { | ||||||||||
value interface{} | ||||||||||
} | ||||||||||
|
||||||||||
introspectionResult struct { | ||||||||||
exports map[string]introspectedExport | ||||||||||
} | ||||||||||
) | ||||||||||
|
||||||||||
// HasExport implements runtime_registry.IntrospectedExport. | ||||||||||
func (i introspectedExport) HasExport() bool { | ||||||||||
return i.value != nil | ||||||||||
} | ||||||||||
|
||||||||||
// Value implements runtime_registry.IntrospectedExport. | ||||||||||
func (i introspectedExport) Value() interface{} { | ||||||||||
return i.value | ||||||||||
} | ||||||||||
|
||||||||||
// ValueAsMap implements runtime_registry.IntrospectedExport. | ||||||||||
func (i introspectedExport) ValueAsMap() map[string]interface{} { | ||||||||||
if i.value == nil { | ||||||||||
return map[string]interface{}{} | ||||||||||
} | ||||||||||
return i.value.(map[string]interface{}) | ||||||||||
} | ||||||||||
|
||||||||||
// GetExport implements runtime_registry.IntrospectionResult. | ||||||||||
func (i introspectionResult) GetExport(name string) runtimesRegistry.IntrospectedExport { | ||||||||||
return i.exports[name] | ||||||||||
} | ||||||||||
|
||||||||||
func (i introspectionResult) recordExport(name string, value interface{}) { | ||||||||||
i.exports[name] = introspectedExport{ | ||||||||||
value: value, | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
// GetConsoleError implements runtime_registry.Result. | ||||||||||
func (a *actionResult) GetConsoleError() []interface{} { | ||||||||||
return a.ConsoleError | ||||||||||
|
@@ -82,69 +118,46 @@ func newGojaRunner() runtimesRegistry.Runner { | |||||||||
return &runner | ||||||||||
} | ||||||||||
|
||||||||||
func (e *gojaRunnerV1) Execute(ctx context.Context, workflow runtimesRegistry.WorkflowDescriptor, startOptions runtimesRegistry.StartOptions) (runtimesRegistry.Result, error) { | ||||||||||
// Introspect implements runtime_registry.Runner. | ||||||||||
func (e *gojaRunnerV1) Introspect(ctx context.Context, workflow runtimesRegistry.WorkflowDescriptor, options runtimesRegistry.InstrospectionOptions) (runtimesRegistry.IntrospectionResult, error) { | ||||||||||
vm := goja.New() | ||||||||||
_, returnErr := setupVM(ctx, vm, e, workflow) | ||||||||||
|
||||||||||
registry.Enable(vm) | ||||||||||
|
||||||||||
e.maxExecutionTimeout(ctx, vm, workflow.Limits.MaxExecutionDuration) | ||||||||||
vm.SetTimeSource(func() time.Time { return time.Now() }) //static time source | ||||||||||
|
||||||||||
executionResult := &actionResult{ | ||||||||||
ConsoleLog: []interface{}{}, | ||||||||||
ConsoleError: []interface{}{}, | ||||||||||
Context: map[string]interface{}{}, | ||||||||||
if returnErr != nil { | ||||||||||
return nil, returnErr | ||||||||||
} | ||||||||||
module := vm.Get("module").ToObject(vm) | ||||||||||
exports := module.Get("exports").ToObject(vm) | ||||||||||
|
||||||||||
for name, binding := range workflow.Bindings.GlobalModules { | ||||||||||
if module, ok := availableModules[name]; ok { | ||||||||||
module(e, vm, vm.NewObject(), executionResult, binding) | ||||||||||
} | ||||||||||
introspectionResult := introspectionResult{ | ||||||||||
exports: map[string]introspectedExport{}, | ||||||||||
} | ||||||||||
|
||||||||||
vm.Set("kinde", vm.NewObject()) | ||||||||||
for name, binding := range workflow.Bindings.KindeAPIs { | ||||||||||
kindeMountPoint := vm.Get("kinde").(*goja.Object) | ||||||||||
if apiFunc, ok := kindeAPIs[name]; ok { | ||||||||||
kindeMountPoint.Set(name, e.callRegisteredAPI(binding, apiFunc)) | ||||||||||
for _, exportToIntrospect := range options.Exports { | ||||||||||
exportIntrospect := exports.Get(exportToIntrospect) | ||||||||||
if exportIntrospect != nil { | ||||||||||
mapped := exportIntrospect.Export() | ||||||||||
introspectionResult.recordExport(exportToIntrospect, mapped) | ||||||||||
} else { | ||||||||||
introspectionResult.recordExport(exportToIntrospect, nil) | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
workflowHash := workflow.GetHash() | ||||||||||
program, err := e.Cache.cacheProgram(workflowHash, func() (*goja.Program, error) { | ||||||||||
ast, err := goja.Parse("main", string(workflow.ProcessedSource.Source)) | ||||||||||
|
||||||||||
if err != nil { | ||||||||||
return nil, fmt.Errorf("error parsing %w", err) | ||||||||||
} | ||||||||||
|
||||||||||
program, err := goja.CompileAST(ast, false) | ||||||||||
|
||||||||||
if err != nil { | ||||||||||
return nil, fmt.Errorf("error compiling %w", err) | ||||||||||
} | ||||||||||
return introspectionResult, nil | ||||||||||
} | ||||||||||
|
||||||||||
return program, nil | ||||||||||
func (e *gojaRunnerV1) Execute(ctx context.Context, workflow runtimesRegistry.WorkflowDescriptor, startOptions runtimesRegistry.StartOptions) (runtimesRegistry.ExecutionResult, error) { | ||||||||||
|
||||||||||
}) | ||||||||||
vm := goja.New() | ||||||||||
executionResult, returnErr := setupVM(ctx, vm, e, workflow) | ||||||||||
|
||||||||||
if err != nil { | ||||||||||
return nil, err | ||||||||||
} | ||||||||||
|
||||||||||
_, err = vm.RunProgram(program) | ||||||||||
if err != nil { | ||||||||||
return nil, fmt.Errorf("%v", err.Error()) | ||||||||||
if returnErr != nil { | ||||||||||
return executionResult, returnErr | ||||||||||
Comment on lines
+154
to
+155
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ensure consistent error handling between Introspect and Execute methods In the Apply this diff to standardize error handling: if returnErr != nil {
- return executionResult, returnErr
+ return nil, returnErr
} Committable suggestion
Suggested change
|
||||||||||
} | ||||||||||
|
||||||||||
module := vm.Get("module").ToObject(vm) | ||||||||||
exports := module.Get("exports").ToObject(vm) | ||||||||||
|
||||||||||
settingsExport := exports.Get("workflowSettings") | ||||||||||
if settingsExport != nil { | ||||||||||
executionResult.Context["workflowSettings"] = settingsExport.Export() | ||||||||||
} | ||||||||||
|
||||||||||
defaultExport := exports.Get("default") | ||||||||||
if defaultExport == nil { | ||||||||||
return nil, fmt.Errorf("no default export") | ||||||||||
|
@@ -196,6 +209,61 @@ func (e *gojaRunnerV1) Execute(ctx context.Context, workflow runtimesRegistry.Wo | |||||||||
return executionResult, nil | ||||||||||
} | ||||||||||
|
||||||||||
func setupVM(ctx context.Context, vm *goja.Runtime, runner *gojaRunnerV1, workflow runtimesRegistry.WorkflowDescriptor) (*actionResult, error) { | ||||||||||
registry.Enable(vm) | ||||||||||
|
||||||||||
runner.maxExecutionTimeout(ctx, vm, workflow.Limits.MaxExecutionDuration) | ||||||||||
vm.SetTimeSource(func() time.Time { return time.Now() }) | ||||||||||
|
||||||||||
executionResult := &actionResult{ | ||||||||||
ConsoleLog: []interface{}{}, | ||||||||||
ConsoleError: []interface{}{}, | ||||||||||
Context: map[string]interface{}{}, | ||||||||||
} | ||||||||||
|
||||||||||
for name, binding := range workflow.Bindings.GlobalModules { | ||||||||||
if module, ok := availableModules[name]; ok { | ||||||||||
module(runner, vm, vm.NewObject(), executionResult, binding) | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
vm.Set("kinde", vm.NewObject()) | ||||||||||
for name, binding := range workflow.Bindings.KindeAPIs { | ||||||||||
kindeMountPoint := vm.Get("kinde").(*goja.Object) | ||||||||||
if apiFunc, ok := kindeAPIs[name]; ok { | ||||||||||
kindeMountPoint.Set(name, runner.callRegisteredAPI(binding, apiFunc)) | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
workflowHash := workflow.GetHash() | ||||||||||
program, err := runner.Cache.cacheProgram(workflowHash, func() (*goja.Program, error) { | ||||||||||
ast, err := goja.Parse("main", string(workflow.ProcessedSource.Source)) | ||||||||||
|
||||||||||
if err != nil { | ||||||||||
return nil, fmt.Errorf("error parsing %w", err) | ||||||||||
} | ||||||||||
|
||||||||||
program, err := goja.CompileAST(ast, false) | ||||||||||
|
||||||||||
if err != nil { | ||||||||||
return nil, fmt.Errorf("error compiling %w", err) | ||||||||||
} | ||||||||||
|
||||||||||
return program, nil | ||||||||||
|
||||||||||
}) | ||||||||||
|
||||||||||
if err != nil { | ||||||||||
return nil, err | ||||||||||
} | ||||||||||
|
||||||||||
_, err = vm.RunProgram(program) | ||||||||||
if err != nil { | ||||||||||
return nil, fmt.Errorf("%v", err.Error()) | ||||||||||
} | ||||||||||
return executionResult, nil | ||||||||||
} | ||||||||||
|
||||||||||
func (*gojaRunnerV1) maxExecutionTimeout(ctx context.Context, vm *goja.Runtime, maxExecutionDuration time.Duration) { | ||||||||||
go func() { | ||||||||||
timer := time.NewTimer(maxExecutionDuration) | ||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,28 @@ | ||||||||||||||
package project_bundler | ||||||||||||||
|
||||||||||||||
type ( | ||||||||||||||
ProjectConfiguration struct { | ||||||||||||||
Version string `json:"version"` | ||||||||||||||
RootDir string `json:"root_dir"` | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
KindeWorkflow struct { | ||||||||||||||
WorkflowRootDirectory string `json:"workflow_root_directory"` | ||||||||||||||
} | ||||||||||||||
KindeWorkflows struct { | ||||||||||||||
Workflows []KindeWorkflow `json:"workflows"` | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
KindeEnvironment struct { | ||||||||||||||
Workflows KindeWorkflows `json:"workflows"` | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
KindeProject struct { | ||||||||||||||
Configuration ProjectConfiguration `json:"configuration"` | ||||||||||||||
Environment KindeEnvironment `json:"environment"` | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
ProjectBundler interface { | ||||||||||||||
Discover() KindeProject | ||||||||||||||
} | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider enhancing the ProjectBundler interface for better error handling and context support. While the
Here's a suggested improvement: type ProjectBundler interface {
- Discover() KindeProject
+ DiscoverProject(ctx context.Context) (KindeProject, error)
} This change would allow for more robust error handling and support for cancellation or timeouts via the context. Committable suggestion
Suggested change
|
||||||||||||||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package project_bundler | ||
|
||
import "testing" | ||
|
||
func Test_ProjectBunler(t *testing.T) { | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix typo in function name and implement test logic. There are two issues with the test function:
Please apply the following changes:
-func Test_ProjectBunler(t *testing.T) {
+func Test_ProjectBundler(t *testing.T) {
func Test_ProjectBundler(t *testing.T) {
// TODO: Implement test cases for ProjectBundler
t.Run("Test case description", func(t *testing.T) {
// Add assertions here
})
} Would you like assistance in designing and implementing specific test cases for the ProjectBundler? |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -3,7 +3,7 @@ package runtime_registry | |||||||||||||||||||||||||
import ( | ||||||||||||||||||||||||||
"context" | ||||||||||||||||||||||||||
"crypto/sha256" | ||||||||||||||||||||||||||
"encoding/base64" | ||||||||||||||||||||||||||
"encoding/base32" | ||||||||||||||||||||||||||
"fmt" | ||||||||||||||||||||||||||
"time" | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
|
@@ -21,11 +21,6 @@ type ( | |||||||||||||||||||||||||
Arguments []interface{} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
runtimeReference struct { | ||||||||||||||||||||||||||
Name string `json:"name"` | ||||||||||||||||||||||||||
Version int `json:"version"` | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
CodeDescriptor struct { | ||||||||||||||||||||||||||
Source []byte `json:"source"` | ||||||||||||||||||||||||||
SourceType SourceContentType `json:"source_type"` | ||||||||||||||||||||||||||
|
@@ -50,21 +45,35 @@ type ( | |||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
WorkflowDescriptor struct { | ||||||||||||||||||||||||||
runtime runtimeReference | ||||||||||||||||||||||||||
ProcessedSource CodeDescriptor `json:"processed_source"` | ||||||||||||||||||||||||||
Bindings Bindings `json:"bindings"` | ||||||||||||||||||||||||||
Limits RuntimeLimits `json:"runtime_limits"` | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Result interface { | ||||||||||||||||||||||||||
ExecutionResult interface { | ||||||||||||||||||||||||||
GetExitResult() interface{} | ||||||||||||||||||||||||||
GetConsoleLog() []interface{} | ||||||||||||||||||||||||||
GetConsoleError() []interface{} | ||||||||||||||||||||||||||
GetContext() map[string]interface{} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
Comment on lines
+53
to
58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Issues Found
Analysis chainApprove renaming of Result to ExecutionResult The renaming of To ensure all usages of this interface have been updated, please run the following script: Further Verification Needed Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Description: Verify all occurrences of the old interface name have been updated.
# Test: Search for any remaining instances of the old interface name. Expect: No results.
rg --type go '\bResult\b' --glob '!registry/runtimeRegistry.go'
# Test: Confirm the new interface name is used correctly. Expect: Occurrences of ExecutionResult.
rg --type go '\bExecutionResult\b'
Length of output: 412 Script: #!/bin/bash
# Description: Verify all occurrences of the old interface name have been updated in both source and test files.
# Test: Search for any remaining instances of the old interface name. Expect: No results.
rg --type go '\bResult\b' --glob '!registry/runtimeRegistry.go'
# Test: Confirm the new interface name is used correctly. Expect: Occurrences of ExecutionResult.
rg --type go '\bExecutionResult\b'
Length of output: 985 Script: #!/bin/bash
# Description: Ensure that Runner interface tests include the new Introspect method.
# Test: Search for tests of the Runner interface to confirm Introspect method is included.
rg --type go 'func TestRunner.*Introspect' --glob '*_test.go'
Length of output: 63 |
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Comment on lines
+53
to
59
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Refactor accessor method names to follow Go conventions In Go, it's idiomatic to omit the
Apply this diff to update the method names: ExecutionResult interface {
- GetExitResult() interface{}
- GetConsoleLog() []interface{}
- GetConsoleError() []interface{}
- GetContext() map[string]interface{}
+ ExitResult() interface{}
+ ConsoleLog() []interface{}
+ ConsoleError() []interface{}
+ Context() map[string]interface{}
} Committable suggestion
Suggested change
|
||||||||||||||||||||||||||
IntrospectedExport interface { | ||||||||||||||||||||||||||
HasExport() bool | ||||||||||||||||||||||||||
Value() interface{} | ||||||||||||||||||||||||||
ValueAsMap() map[string]interface{} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
IntrospectionResult interface { | ||||||||||||||||||||||||||
GetExport(string) IntrospectedExport | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
Comment on lines
+66
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Rename 'GetExport' to 'Export' in 'IntrospectionResult' interface Following Go's naming conventions, consider renaming the Apply this diff to rename the method: IntrospectionResult interface {
- GetExport(string) IntrospectedExport
+ Export(string) IntrospectedExport
} Committable suggestion
Suggested change
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
InstrospectionOptions struct { | ||||||||||||||||||||||||||
Exports []string | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Approve new struct, but correct the spelling The new Please correct the spelling as follows: -InstrospectionOptions struct {
+IntrospectionOptions struct {
Exports []string
} Committable suggestion
Suggested change
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Runner interface { | ||||||||||||||||||||||||||
Execute(ctx context.Context, workflow WorkflowDescriptor, startOptions StartOptions) (Result, error) | ||||||||||||||||||||||||||
Execute(ctx context.Context, workflow WorkflowDescriptor, startOptions StartOptions) (ExecutionResult, error) | ||||||||||||||||||||||||||
Introspect(ctx context.Context, workflow WorkflowDescriptor, options InstrospectionOptions) (IntrospectionResult, error) | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Approve interface changes, but correct the spelling The updates to the However, there's a spelling error in the Runner interface {
Execute(ctx context.Context, workflow WorkflowDescriptor, startOptions StartOptions) (ExecutionResult, error)
- Introspect(ctx context.Context, workflow WorkflowDescriptor, options InstrospectionOptions) (IntrospectionResult, error)
+ Introspect(ctx context.Context, workflow WorkflowDescriptor, options IntrospectionOptions) (IntrospectionResult, error)
} Committable suggestion
Suggested change
|
||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
@@ -85,9 +94,8 @@ func ResolveRuntime(name string) (Runner, error) { | |||||||||||||||||||||||||
|
||||||||||||||||||||||||||
// Returns a hash of the workflow descriptor | ||||||||||||||||||||||||||
func (wd *WorkflowDescriptor) GetHash() string { | ||||||||||||||||||||||||||
base := fmt.Sprintf("%v-%v", wd.ProcessedSource.BuildHash, string(wd.ProcessedSource.Source)) | ||||||||||||||||||||||||||
sha := sha256.New() | ||||||||||||||||||||||||||
sha.Write([]byte(base)) | ||||||||||||||||||||||||||
result := base64.StdEncoding.EncodeToString(sha.Sum(nil)) | ||||||||||||||||||||||||||
sha.Write([]byte(wd.ProcessedSource.Source)) | ||||||||||||||||||||||||||
result := base32.StdEncoding.EncodeToString(sha.Sum(nil)) | ||||||||||||||||||||||||||
return fmt.Sprintf("%v", result) | ||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"version": "2024-12-09", | ||
"rootDir": "kindeSrc" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const hello = 'hello'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider safer type assertion in ValueAsMap method
The
ValueAsMap
method uses a type assertion that could potentially panic if thevalue
is not of typemap[string]interface{}
.Consider using a type assertion with the
ok
idiom to safely handle cases where the type assertion may fail:Committable suggestion