• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

forst-lang / forst / 16231277551

11 Jul 2025 11:00PM UTC coverage: 37.311% (-3.3%) from 40.653%
16231277551

Pull #25

github

haveyaseen
fix: add build dependency to sidecar-local task

- Ensure Go binary is built before running sidecar example
- Add build task as dependency to example:sidecar-local
- Fixes issue where sidecar example would fail if binary not built
Pull Request #25: feat: JS/TS integration

177 of 1342 new or added lines in 15 files covered. (13.19%)

17 existing lines in 3 files now uncovered.

4143 of 11104 relevant lines covered (37.31%)

6.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/forst/internal/executor/executor.go
1
package executor
2

3
import (
4
        "bufio"
5
        "context"
6
        "encoding/json"
7
        "fmt"
8
        "math/rand"
9
        "os"
10
        "os/exec"
11
        "path/filepath"
12
        "sync"
13

14
        "forst/cmd/forst/compiler"
15
        "forst/internal/configiface"
16
        "forst/internal/discovery"
17
        "forst/internal/generators"
18
        "forst/internal/lexer"
19
        "forst/internal/parser"
20
        transformer_go "forst/internal/transformer/go"
21
        "forst/internal/typechecker"
22

23
        "bytes"
24

25
        logrus "github.com/sirupsen/logrus"
26
)
27

28
// generateRandomString generates a random string of specified length
NEW
29
func generateRandomString(length int) string {
×
NEW
30
        const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
×
NEW
31
        b := make([]byte, length)
×
NEW
32
        for i := range b {
×
NEW
33
                b[i] = charset[rand.Intn(len(charset))]
×
NEW
34
        }
×
NEW
35
        return string(b)
×
36
}
37

38
// FunctionExecutor handles execution of Forst functions
39
type FunctionExecutor struct {
40
        rootDir       string
41
        compiler      *compiler.Compiler
42
        log           *logrus.Logger
43
        cache         map[string]*CompiledFunction
44
        mu            sync.RWMutex
45
        config        configiface.ForstConfigIface
46
        moduleManager *GoModuleManager
47
}
48

49
// CompiledFunction represents a compiled Forst function
50
type CompiledFunction struct {
51
        PackageName       string
52
        FunctionName      string
53
        GoCode            string
54
        FilePath          string
55
        SupportsStreaming bool
56
        Parameters        []discovery.ParameterInfo
57
}
58

59
// ExecutionResult represents the result of a function execution
60
type ExecutionResult struct {
61
        Success bool            `json:"success"`
62
        Output  string          `json:"output,omitempty"`
63
        Error   string          `json:"error,omitempty"`
64
        Result  json.RawMessage `json:"result,omitempty"`
65
}
66

67
// StreamingResult represents a streaming result
68
type StreamingResult struct {
69
        Data   interface{} `json:"data"`
70
        Status string      `json:"status"`
71
        Error  string      `json:"error,omitempty"`
72
}
73

74
// NewFunctionExecutor creates a new function executor
NEW
75
func NewFunctionExecutor(rootDir string, comp *compiler.Compiler, log *logrus.Logger, config configiface.ForstConfigIface) *FunctionExecutor {
×
NEW
76
        return &FunctionExecutor{
×
NEW
77
                rootDir:       rootDir,
×
NEW
78
                compiler:      comp,
×
NEW
79
                log:           log,
×
NEW
80
                cache:         make(map[string]*CompiledFunction),
×
NEW
81
                config:        config,
×
NEW
82
                moduleManager: NewGoModuleManager(log),
×
NEW
83
        }
×
NEW
84
}
×
85

86
// ExecuteFunction executes a Forst function with the given arguments
NEW
87
func (e *FunctionExecutor) ExecuteFunction(packageName, functionName string, args json.RawMessage) (*ExecutionResult, error) {
×
NEW
88
        e.log.Debugf("ExecuteFunction: %s.%s", packageName, functionName)
×
NEW
89

×
NEW
90
        // Get or compile the function
×
NEW
91
        compiledFn, err := e.getOrCompileFunction(packageName, functionName)
×
NEW
92
        e.log.Debugf("Compiled function: %v", compiledFn)
×
NEW
93
        if err != nil {
×
NEW
94
                return nil, fmt.Errorf("failed to get function: %v", err)
×
NEW
95
        }
×
96

97
        // Create temporary Go module with the function call
NEW
98
        tempDir, err := e.createTempGoFile(compiledFn, args)
×
NEW
99
        e.log.Tracef("Temp dir: %s", tempDir)
×
NEW
100
        if err != nil {
×
NEW
101
                return nil, fmt.Errorf("failed to create temp dir: %v", err)
×
NEW
102
        }
×
NEW
103
        defer os.RemoveAll(tempDir)
×
NEW
104

×
NEW
105
        // Debug: Let's look at the actual generated files
×
NEW
106
        e.log.Tracef("Generated Go code:\n%s", compiledFn.GoCode)
×
NEW
107

×
NEW
108
        // List the contents of the temp directory
×
NEW
109
        entries, err := os.ReadDir(tempDir)
×
NEW
110
        if err == nil {
×
NEW
111
                e.log.Tracef("Temp directory contents:")
×
NEW
112
                for _, entry := range entries {
×
NEW
113
                        if entry.IsDir() {
×
NEW
114
                                subEntries, _ := os.ReadDir(filepath.Join(tempDir, entry.Name()))
×
NEW
115
                                for _, subEntry := range subEntries {
×
NEW
116
                                        e.log.Tracef("  %s/%s", entry.Name(), subEntry.Name())
×
NEW
117
                                }
×
NEW
118
                        } else {
×
NEW
119
                                e.log.Tracef("  %s", entry.Name())
×
NEW
120
                        }
×
121
                }
122
        }
123

124
        // Debug: Read and log the actual main.go file
NEW
125
        mainGoPath := filepath.Join(tempDir, "main.go")
×
NEW
126
        if mainGoContent, err := os.ReadFile(mainGoPath); err == nil {
×
NEW
127
                e.log.Tracef("Generated main.go content:\n%s", string(mainGoContent))
×
NEW
128
        } else {
×
NEW
129
                e.log.Errorf("Failed to read main.go: %v", err)
×
NEW
130
        }
×
131

132
        // Debug: Check log level
NEW
133
        e.log.Infof("Current log level: %v", e.log.GetLevel())
×
NEW
134

×
NEW
135
        // Debug: Check if the echo function has parameters
×
NEW
136
        e.log.Infof("Echo function parameters: %v", compiledFn.Parameters)
×
NEW
137

×
NEW
138
        // Execute the Go code
×
NEW
139
        hasParams := len(compiledFn.Parameters) > 0
×
NEW
140
        e.log.Infof("executeGoCode: hasParams=%v, args=%s", hasParams, string(args))
×
NEW
141
        output, err := e.executeGoCode(tempDir, args, hasParams, compiledFn.Parameters)
×
NEW
142
        if err != nil {
×
NEW
143
                e.log.Errorf("Failed to execute Go code: %v", err)
×
NEW
144
                return nil, fmt.Errorf("failed to execute Go code: %v", err)
×
NEW
145
        }
×
146

NEW
147
        e.log.Infof("Output: %s", output)
×
NEW
148

×
NEW
149
        // Parse the output
×
NEW
150
        result, err := e.parseExecutionOutput(output)
×
NEW
151
        if err != nil {
×
NEW
152
                e.log.Errorf("Failed to parse output: %v", err)
×
NEW
153
                return nil, fmt.Errorf("failed to parse output: %v", err)
×
NEW
154
        }
×
155

NEW
156
        return result, nil
×
157
}
158

159
// ExecuteStreamingFunction executes a Forst function with streaming support
NEW
160
func (e *FunctionExecutor) ExecuteStreamingFunction(ctx context.Context, packageName, functionName string, args json.RawMessage) (<-chan StreamingResult, error) {
×
NEW
161
        // Get or compile the function
×
NEW
162
        compiledFn, err := e.getOrCompileFunction(packageName, functionName)
×
NEW
163
        if err != nil {
×
NEW
164
                return nil, fmt.Errorf("failed to get function: %v", err)
×
NEW
165
        }
×
166

NEW
167
        if !compiledFn.SupportsStreaming {
×
NEW
168
                return nil, fmt.Errorf("function %s does not support streaming", functionName)
×
NEW
169
        }
×
170

171
        // Create temporary Go module with streaming function call
NEW
172
        tempDir, err := e.createStreamingTempGoFile(compiledFn, args)
×
NEW
173
        if err != nil {
×
NEW
174
                return nil, fmt.Errorf("failed to create temp dir: %v", err)
×
NEW
175
        }
×
176

177
        // Execute the Go code with streaming
NEW
178
        return e.executeStreamingGoCode(ctx, tempDir, args, len(compiledFn.Parameters) > 0)
×
179
}
180

181
// getOrCompileFunction gets a compiled function from cache or compiles it
NEW
182
func (e *FunctionExecutor) getOrCompileFunction(packageName, functionName string) (*CompiledFunction, error) {
×
NEW
183
        cacheKey := fmt.Sprintf("%s.%s", packageName, functionName)
×
NEW
184

×
NEW
185
        e.mu.RLock()
×
NEW
186
        if cached, exists := e.cache[cacheKey]; exists {
×
NEW
187
                e.mu.RUnlock()
×
NEW
188
                return cached, nil
×
NEW
189
        }
×
NEW
190
        e.mu.RUnlock()
×
NEW
191

×
NEW
192
        // Compile the function
×
NEW
193
        compiledFn, err := e.compileFunction(packageName, functionName)
×
NEW
194
        if err != nil {
×
NEW
195
                return nil, err
×
NEW
196
        }
×
197

198
        // Cache the compiled function
NEW
199
        e.mu.Lock()
×
NEW
200
        e.cache[cacheKey] = compiledFn
×
NEW
201
        e.mu.Unlock()
×
NEW
202

×
NEW
203
        return compiledFn, nil
×
204
}
205

206
// compileFunction compiles a Forst function to Go code
NEW
207
func (e *FunctionExecutor) compileFunction(packageName, functionName string) (*CompiledFunction, error) {
×
NEW
208
        // Find the Forst file containing the function
×
NEW
209
        filePath, err := e.findFunctionFile(packageName, functionName)
×
NEW
210
        if err != nil {
×
NEW
211
                return nil, fmt.Errorf("failed to find function file: %v", err)
×
NEW
212
        }
×
213

214
        // Read and parse the source file
NEW
215
        source, err := os.ReadFile(filePath)
×
NEW
216
        if err != nil {
×
NEW
217
                return nil, fmt.Errorf("error reading file: %v", err)
×
NEW
218
        }
×
219

NEW
220
        l := lexer.New(source, filePath, e.log)
×
NEW
221
        tokens := l.Lex()
×
NEW
222

×
NEW
223
        psr := parser.New(tokens, filePath, e.log)
×
NEW
224
        forstNodes, err := psr.ParseFile()
×
NEW
225
        if err != nil {
×
NEW
226
                return nil, err
×
NEW
227
        }
×
228

NEW
229
        checker := typechecker.New(e.log, false)
×
NEW
230
        if err := checker.CheckTypes(forstNodes); err != nil {
×
NEW
231
                e.log.Error("Encountered error checking types: ", err)
×
NEW
232
                checker.DebugPrintCurrentScope()
×
NEW
233
                return nil, err
×
NEW
234
        }
×
235

236
        // Use ExportReturnStructFields=true for executor/dev server
NEW
237
        transformer := transformer_go.New(checker, e.log, true)
×
NEW
238
        goAST, err := transformer.TransformForstFileToGo(forstNodes)
×
NEW
239
        if err != nil {
×
NEW
240
                return nil, fmt.Errorf("failed to transform Forst file to Go: %v", err)
×
NEW
241
        }
×
NEW
242
        goCode, err := generators.GenerateGoCode(goAST)
×
NEW
243
        if err != nil {
×
NEW
244
                return nil, fmt.Errorf("failed to generate Go code: %v", err)
×
NEW
245
        }
×
246

247
        // Extract function information
NEW
248
        fnInfo, err := e.getFunctionInfo(packageName, functionName)
×
NEW
249
        if err != nil {
×
NEW
250
                return nil, fmt.Errorf("failed to get function info: %v", err)
×
NEW
251
        }
×
252

NEW
253
        return &CompiledFunction{
×
NEW
254
                PackageName:       packageName,
×
NEW
255
                FunctionName:      functionName,
×
NEW
256
                GoCode:            goCode,
×
NEW
257
                FilePath:          filePath,
×
NEW
258
                SupportsStreaming: fnInfo.SupportsStreaming,
×
NEW
259
                Parameters:        fnInfo.Parameters, // Populate Parameters
×
NEW
260
        }, nil
×
261
}
262

263
// findFunctionFile finds the Forst file containing the specified function
NEW
264
func (e *FunctionExecutor) findFunctionFile(packageName, functionName string) (string, error) {
×
NEW
265
        // This is a simplified implementation
×
NEW
266
        // In a real implementation, you'd use the discovery package to find the file
×
NEW
267
        discoverer := discovery.NewDiscoverer(e.rootDir, e.log, e.config)
×
NEW
268
        functions, err := discoverer.DiscoverFunctions()
×
NEW
269
        if err != nil {
×
NEW
270
                return "", fmt.Errorf("failed to discover functions: %v", err)
×
NEW
271
        }
×
272

NEW
273
        pkgFuncs, exists := functions[packageName]
×
NEW
274
        if !exists {
×
NEW
275
                return "", fmt.Errorf("package %s not found", packageName)
×
NEW
276
        }
×
277

NEW
278
        fnInfo, exists := pkgFuncs[functionName]
×
NEW
279
        if !exists {
×
NEW
280
                return "", fmt.Errorf("function %s not found in package %s", functionName, packageName)
×
NEW
281
        }
×
282

NEW
283
        return fnInfo.FilePath, nil
×
284
}
285

286
// getFunctionInfo gets information about a function
NEW
287
func (e *FunctionExecutor) getFunctionInfo(packageName, functionName string) (*discovery.FunctionInfo, error) {
×
NEW
288
        discoverer := discovery.NewDiscoverer(e.rootDir, e.log, e.config)
×
NEW
289
        functions, err := discoverer.DiscoverFunctions()
×
NEW
290
        if err != nil {
×
NEW
291
                return nil, fmt.Errorf("failed to discover functions: %v", err)
×
NEW
292
        }
×
293

NEW
294
        pkgFuncs, exists := functions[packageName]
×
NEW
295
        if !exists {
×
NEW
296
                return nil, fmt.Errorf("package %s not found", packageName)
×
NEW
297
        }
×
298

NEW
299
        fnInfo, exists := pkgFuncs[functionName]
×
NEW
300
        if !exists {
×
NEW
301
                return nil, fmt.Errorf("function %s not found in package %s", functionName, packageName)
×
NEW
302
        }
×
303

NEW
304
        return &fnInfo, nil
×
305
}
306

307
// createTempGoFile creates a temporary Go file that calls the specified function
NEW
308
func (e *FunctionExecutor) createTempGoFile(compiledFn *CompiledFunction, args json.RawMessage) (string, error) {
×
NEW
309
        e.log.Debugf("createTempGoFile: compiledFn.Parameters=%v, len=%d", compiledFn.Parameters, len(compiledFn.Parameters))
×
NEW
310

×
NEW
311
        config := &ModuleConfig{
×
NEW
312
                ModuleName:     fmt.Sprintf("exec-%s", generateRandomString(8)),
×
NEW
313
                PackageName:    compiledFn.PackageName,
×
NEW
314
                FunctionName:   compiledFn.FunctionName,
×
NEW
315
                GoCode:         compiledFn.GoCode,
×
NEW
316
                SupportsParams: len(compiledFn.Parameters) > 0,
×
NEW
317
                Parameters:     compiledFn.Parameters,
×
NEW
318
                Args:           args,
×
NEW
319
                IsStreaming:    false,
×
NEW
320
        }
×
NEW
321

×
NEW
322
        e.log.Debugf("ModuleConfig: SupportsParams=%v, Parameters=%v", config.SupportsParams, config.Parameters)
×
NEW
323

×
NEW
324
        tempDir, err := e.moduleManager.CreateModule(config)
×
NEW
325
        e.log.Debugf("Created temp dir: %s", tempDir)
×
NEW
326
        if err != nil {
×
NEW
327
                e.log.Errorf("Failed to create module: %v", err)
×
NEW
328
                return "", err
×
NEW
329
        }
×
330

NEW
331
        return tempDir, nil
×
332
}
333

334
// createStreamingTempGoFile creates a temporary Go file for streaming execution
NEW
335
func (e *FunctionExecutor) createStreamingTempGoFile(compiledFn *CompiledFunction, args json.RawMessage) (string, error) {
×
NEW
336
        config := &ModuleConfig{
×
NEW
337
                ModuleName:     fmt.Sprintf("streaming-%s", generateRandomString(8)),
×
NEW
338
                PackageName:    compiledFn.PackageName,
×
NEW
339
                FunctionName:   compiledFn.FunctionName,
×
NEW
340
                GoCode:         compiledFn.GoCode,
×
NEW
341
                SupportsParams: len(compiledFn.Parameters) > 0,
×
NEW
342
                Parameters:     compiledFn.Parameters,
×
NEW
343
                Args:           args,
×
NEW
344
                IsStreaming:    true,
×
NEW
345
        }
×
NEW
346

×
NEW
347
        return e.moduleManager.CreateModule(config)
×
NEW
348
}
×
349

350
// executeGoCode executes Go code and returns the output
NEW
351
func (e *FunctionExecutor) executeGoCode(tempDir string, args json.RawMessage, hasParams bool, params ...interface{}) (string, error) {
×
NEW
352
        var cmd *exec.Cmd
×
NEW
353
        if hasParams {
×
NEW
354
                e.log.Tracef("Executing Go program with args: %s", string(args))
×
NEW
355
                cmd = exec.Command("go", "run", ".")
×
NEW
356
                cmd.Dir = tempDir
×
NEW
357
                // Set up output buffers
×
NEW
358
                var stdoutBuf, stderrBuf bytes.Buffer
×
NEW
359
                cmd.Stdout = &stdoutBuf
×
NEW
360
                cmd.Stderr = &stderrBuf
×
NEW
361
                // Set up stdin to provide the args
×
NEW
362
                stdin, err := cmd.StdinPipe()
×
NEW
363
                if err != nil {
×
NEW
364
                        return "", fmt.Errorf("failed to create stdin pipe: %v", err)
×
NEW
365
                }
×
366
                // Start the command first
NEW
367
                if err := cmd.Start(); err != nil {
×
NEW
368
                        e.log.Errorf("Command start failed: %v", err)
×
NEW
369
                        return "", fmt.Errorf("failed to start command: %v", err)
×
NEW
370
                }
×
371
                // Write args to stdin synchronously
NEW
372
                data := []byte("{}")
×
NEW
373
                if len(args) == 0 || string(args) == "null" {
×
NEW
374
                        data = []byte("{}")
×
NEW
375
                } else {
×
NEW
376
                        data = args
×
NEW
377
                }
×
NEW
378
                n, err := stdin.Write(data)
×
NEW
379
                if err != nil {
×
NEW
380
                        e.log.Errorf("Failed to write to stdin: %v", err)
×
NEW
381
                } else {
×
NEW
382
                        e.log.Debugf("Wrote %d bytes to stdin", n)
×
NEW
383
                }
×
NEW
384
                stdin.Close()
×
NEW
385
                // Wait for the command to complete
×
NEW
386
                err = cmd.Wait()
×
NEW
387
                output := stdoutBuf.String() + stderrBuf.String()
×
NEW
388
                if err != nil {
×
NEW
389
                        e.log.Errorf("Go program failed: %v", err)
×
NEW
390
                        return "", fmt.Errorf("execution failed: %v, output: %s", err, output)
×
NEW
391
                }
×
NEW
392
                return output, nil
×
NEW
393
        } else {
×
NEW
394
                e.log.Tracef("Executing Go program without args")
×
NEW
395
                cmd = exec.Command("go", "run", ".")
×
NEW
396
                cmd.Dir = tempDir
×
NEW
397
                output, err := cmd.CombinedOutput()
×
NEW
398
                if err != nil {
×
NEW
399
                        e.log.Errorf("Go program failed: %v", err)
×
NEW
400
                        return "", fmt.Errorf("execution failed: %v, output: %s", err, string(output))
×
NEW
401
                }
×
NEW
402
                return string(output), nil
×
403
        }
404
}
405

406
// executeStreamingGoCode executes Go code with streaming support
NEW
407
func (e *FunctionExecutor) executeStreamingGoCode(ctx context.Context, tempDir string, args json.RawMessage, hasParams bool) (<-chan StreamingResult, error) {
×
NEW
408
        results := make(chan StreamingResult, 100)
×
NEW
409

×
NEW
410
        var cmd *exec.Cmd
×
NEW
411
        if hasParams {
×
NEW
412
                cmd = exec.CommandContext(ctx, "go", "run", ".", string(args))
×
NEW
413
        } else {
×
NEW
414
                cmd = exec.CommandContext(ctx, "go", "run", ".")
×
NEW
415
        }
×
NEW
416
        cmd.Dir = tempDir
×
NEW
417
        stdout, err := cmd.StdoutPipe()
×
NEW
418
        if err != nil {
×
NEW
419
                return nil, fmt.Errorf("failed to create stdout pipe: %v", err)
×
NEW
420
        }
×
421

NEW
422
        if err := cmd.Start(); err != nil {
×
NEW
423
                return nil, fmt.Errorf("failed to start command: %v", err)
×
NEW
424
        }
×
425

426
        // Read output in background
NEW
427
        go func() {
×
NEW
428
                defer close(results)
×
NEW
429
                defer cmd.Wait()
×
NEW
430

×
NEW
431
                scanner := bufio.NewScanner(stdout)
×
NEW
432
                for scanner.Scan() {
×
NEW
433
                        line := scanner.Text()
×
NEW
434

×
NEW
435
                        var result StreamingResult
×
NEW
436
                        if err := json.Unmarshal([]byte(line), &result); err != nil {
×
NEW
437
                                results <- StreamingResult{Error: err.Error()}
×
NEW
438
                                continue
×
439
                        }
NEW
440
                        results <- result
×
441
                }
442
        }()
443

NEW
444
        return results, nil
×
445
}
446

447
// parseExecutionOutput parses the output of a function execution
NEW
448
func (e *FunctionExecutor) parseExecutionOutput(output string) (*ExecutionResult, error) {
×
NEW
449
        // Try to parse as JSON first
×
NEW
450
        var result map[string]interface{}
×
NEW
451
        if err := json.Unmarshal([]byte(output), &result); err == nil {
×
NEW
452
                // Extract the result field if it exists
×
NEW
453
                if resultValue, exists := result["result"]; exists {
×
NEW
454
                        // Handle primitive values vs objects/arrays
×
NEW
455
                        switch v := resultValue.(type) {
×
NEW
456
                        case string:
×
NEW
457
                                // For strings, return the raw value (not JSON-encoded)
×
NEW
458
                                return &ExecutionResult{
×
NEW
459
                                        Success: true,
×
NEW
460
                                        Output:  v,
×
NEW
461
                                        Result:  []byte(fmt.Sprintf("%q", v)), // JSON-encoded for Result field
×
NEW
462
                                }, nil
×
NEW
463
                        case float64:
×
NEW
464
                                // For numbers, return the raw value as string
×
NEW
465
                                return &ExecutionResult{
×
NEW
466
                                        Success: true,
×
NEW
467
                                        Output:  fmt.Sprintf("%v", v),
×
NEW
468
                                        Result:  []byte(fmt.Sprintf("%v", v)), // JSON-encoded for Result field
×
NEW
469
                                }, nil
×
NEW
470
                        case int:
×
NEW
471
                                // For integers, return the raw value as string
×
NEW
472
                                return &ExecutionResult{
×
NEW
473
                                        Success: true,
×
NEW
474
                                        Output:  fmt.Sprintf("%d", v),
×
NEW
475
                                        Result:  []byte(fmt.Sprintf("%d", v)), // JSON-encoded for Result field
×
NEW
476
                                }, nil
×
NEW
477
                        case bool:
×
NEW
478
                                // For booleans, return the raw value as string
×
NEW
479
                                return &ExecutionResult{
×
NEW
480
                                        Success: true,
×
NEW
481
                                        Output:  fmt.Sprintf("%t", v),
×
NEW
482
                                        Result:  []byte(fmt.Sprintf("%t", v)), // JSON-encoded for Result field
×
NEW
483
                                }, nil
×
NEW
484
                        default:
×
NEW
485
                                // For objects/arrays, return JSON string
×
NEW
486
                                resultData, _ := json.Marshal(resultValue)
×
NEW
487
                                return &ExecutionResult{
×
NEW
488
                                        Success: true,
×
NEW
489
                                        Output:  string(resultData),
×
NEW
490
                                        Result:  resultData,
×
NEW
491
                                }, nil
×
492
                        }
493
                }
494
                // If no result field, return the entire JSON as output
NEW
495
                resultData, _ := json.Marshal(result)
×
NEW
496
                return &ExecutionResult{
×
NEW
497
                        Success: true,
×
NEW
498
                        Output:  output,
×
NEW
499
                        Result:  resultData,
×
NEW
500
                }, nil
×
501
        }
502

503
        // If not JSON, return as raw output
NEW
504
        return &ExecutionResult{
×
NEW
505
                Success: true,
×
NEW
506
                Output:  output,
×
NEW
507
        }, nil
×
508
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc