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

UiPath / uipathcli / 12432844329

20 Dec 2024 02:07PM UTC coverage: 90.385% (-0.005%) from 90.39%
12432844329

push

github

thschmitt
Test on windows

4926 of 5450 relevant lines covered (90.39%)

1.01 hits per line

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

85.12
/plugin/studio/package_analyze_command.go
1
package studio
2

3
import (
4
        "bufio"
5
        "bytes"
6
        "crypto/rand"
7
        "encoding/json"
8
        "errors"
9
        "fmt"
10
        "io"
11
        "math"
12
        "math/big"
13
        "os"
14
        "path/filepath"
15
        "strings"
16
        "sync"
17
        "time"
18

19
        "github.com/UiPath/uipathcli/log"
20
        "github.com/UiPath/uipathcli/output"
21
        "github.com/UiPath/uipathcli/plugin"
22
        "github.com/UiPath/uipathcli/utils"
23
)
24

25
// The PackageAnalyzeCommand runs static code analyis on the project to detect common errors.
26
type PackageAnalyzeCommand struct {
27
        Exec utils.ExecProcess
28
}
29

30
func (c PackageAnalyzeCommand) Command() plugin.Command {
1✔
31
        return *plugin.NewCommand("studio").
1✔
32
                WithCategory("package", "Package", "UiPath Studio package-related actions").
1✔
33
                WithOperation("analyze", "Analyze Project", "Runs static code analysis on the project to detect common errors").
1✔
34
                WithParameter("source", plugin.ParameterTypeString, "Path to a project.json file or a folder containing project.json file", true).
1✔
35
                WithParameter("treat-warnings-as-errors", plugin.ParameterTypeBoolean, "Treat warnings as errors", false).
1✔
36
                WithParameter("stop-on-rule-violation", plugin.ParameterTypeBoolean, "Fail when any rule is violated", false)
1✔
37
}
1✔
38

39
func (c PackageAnalyzeCommand) Execute(context plugin.ExecutionContext, writer output.OutputWriter, logger log.Logger) error {
1✔
40
        source, err := c.getSource(context)
1✔
41
        if err != nil {
1✔
42
                return err
×
43
        }
×
44
        treatWarningsAsErrors := c.getBoolParameter("treat-warnings-as-errors", context.Parameters)
1✔
45
        stopOnRuleViolation := c.getBoolParameter("stop-on-rule-violation", context.Parameters)
1✔
46
        exitCode, result, err := c.execute(source, treatWarningsAsErrors, stopOnRuleViolation, context.Debug, logger)
1✔
47
        if err != nil {
1✔
48
                return err
×
49
        }
×
50

51
        json, err := json.Marshal(result)
1✔
52
        if err != nil {
1✔
53
                return fmt.Errorf("analyze command failed: %v", err)
×
54
        }
×
55
        err = writer.WriteResponse(*output.NewResponseInfo(200, "200 OK", "HTTP/1.1", map[string][]string{}, bytes.NewReader(json)))
1✔
56
        if err != nil {
1✔
57
                return err
×
58
        }
×
59
        if exitCode != 0 {
2✔
60
                return errors.New("")
1✔
61
        }
1✔
62
        return nil
1✔
63
}
64

65
func (c PackageAnalyzeCommand) execute(source string, treatWarningsAsErrors bool, stopOnRuleViolation bool, debug bool, logger log.Logger) (int, *packageAnalyzeResult, error) {
1✔
66
        if !debug {
2✔
67
                bar := c.newAnalyzingProgressBar(logger)
1✔
68
                defer close(bar)
1✔
69
        }
1✔
70

71
        jsonResultFilePath, err := c.getTemporaryJsonResultFilePath()
1✔
72
        if err != nil {
1✔
73
                return 1, nil, err
×
74
        }
×
75
        defer os.Remove(jsonResultFilePath)
1✔
76

1✔
77
        args := []string{"package", "analyze", source, "--resultPath", jsonResultFilePath}
1✔
78
        if treatWarningsAsErrors {
2✔
79
                args = append(args, "--treatWarningsAsErrors")
1✔
80
        }
1✔
81
        if stopOnRuleViolation {
2✔
82
                args = append(args, "--stopOnRuleViolation")
1✔
83
        }
1✔
84

85
        projectReader := newStudioProjectReader(source)
1✔
86

1✔
87
        uipcli := newUipcli(c.Exec, logger)
1✔
88
        cmd, err := uipcli.Execute(projectReader.GetTargetFramework(), args...)
1✔
89
        if err != nil {
1✔
90
                return 1, nil, err
×
91
        }
×
92
        stdout, err := cmd.StdoutPipe()
1✔
93
        if err != nil {
1✔
94
                return 1, nil, fmt.Errorf("Could not run analyze command: %v", err)
×
95
        }
×
96
        defer stdout.Close()
1✔
97
        stderr, err := cmd.StderrPipe()
1✔
98
        if err != nil {
1✔
99
                return 1, nil, fmt.Errorf("Could not run analyze command: %v", err)
×
100
        }
×
101
        defer stderr.Close()
1✔
102
        err = cmd.Start()
1✔
103
        if err != nil {
1✔
104
                return 1, nil, fmt.Errorf("Could not run analyze command: %v", err)
×
105
        }
×
106

107
        stderrOutputBuilder := new(strings.Builder)
1✔
108
        stderrReader := io.TeeReader(stderr, stderrOutputBuilder)
1✔
109

1✔
110
        var wg sync.WaitGroup
1✔
111
        wg.Add(3)
1✔
112
        go c.readOutput(stdout, logger, &wg)
1✔
113
        go c.readOutput(stderrReader, logger, &wg)
1✔
114
        go c.wait(cmd, &wg)
1✔
115
        wg.Wait()
1✔
116

1✔
117
        violations, err := c.readAnalyzeResult(jsonResultFilePath)
1✔
118
        if err != nil {
1✔
119
                return 1, nil, err
×
120
        }
×
121

122
        exitCode := cmd.ExitCode()
1✔
123
        var result *packageAnalyzeResult
1✔
124
        if exitCode == 0 {
2✔
125
                result = newSucceededPackageAnalyzeResult(violations)
1✔
126
        } else {
2✔
127
                result = newFailedPackageAnalyzeResult(
1✔
128
                        violations,
1✔
129
                        stderrOutputBuilder.String(),
1✔
130
                )
1✔
131
        }
1✔
132
        return exitCode, result, nil
1✔
133
}
134

135
func (c PackageAnalyzeCommand) getTemporaryJsonResultFilePath() (string, error) {
1✔
136
        tempDirectory, err := utils.Directories{}.Temp()
1✔
137
        if err != nil {
1✔
138
                return "", err
×
139
        }
×
140
        fileName := c.randomJsonResultFileName()
1✔
141
        return filepath.Join(tempDirectory, fileName), nil
1✔
142
}
143

144
func (c PackageAnalyzeCommand) randomJsonResultFileName() string {
1✔
145
        value, _ := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
1✔
146
        return "analyzeresult-" + value.String() + ".json"
1✔
147
}
1✔
148

149
func (c PackageAnalyzeCommand) readAnalyzeResult(path string) ([]packageAnalyzeViolation, error) {
1✔
150
        file, err := os.Open(path)
1✔
151
        if err != nil && errors.Is(err, os.ErrNotExist) {
2✔
152
                return []packageAnalyzeViolation{}, nil
1✔
153
        }
1✔
154
        if err != nil {
1✔
155
                return []packageAnalyzeViolation{}, fmt.Errorf("Error reading %s file: %v", filepath.Base(path), err)
×
156
        }
×
157
        defer file.Close()
1✔
158
        byteValue, err := io.ReadAll(file)
1✔
159
        if err != nil {
1✔
160
                return []packageAnalyzeViolation{}, fmt.Errorf("Error reading %s file: %v", filepath.Base(path), err)
×
161
        }
×
162

163
        var result analyzeResultJson
1✔
164
        err = json.Unmarshal(byteValue, &result)
1✔
165
        if err != nil {
1✔
166
                return []packageAnalyzeViolation{}, fmt.Errorf("Error parsing %s file: %v", filepath.Base(path), err)
×
167
        }
×
168
        return c.convertToViolations(result), nil
1✔
169
}
170

171
func (c PackageAnalyzeCommand) convertToViolations(json analyzeResultJson) []packageAnalyzeViolation {
1✔
172
        violations := []packageAnalyzeViolation{}
1✔
173
        for _, entry := range json {
2✔
174
                var activityId *packageAnalyzeActivityId
1✔
175
                if entry.ActivityId != nil {
2✔
176
                        activityId = &packageAnalyzeActivityId{
1✔
177
                                Id:    entry.ActivityId.Id,
1✔
178
                                IdRef: entry.ActivityId.IdRef,
1✔
179
                        }
1✔
180
                }
1✔
181
                var item *packageAnalyzeItem
1✔
182
                if entry.Item != nil {
2✔
183
                        item = &packageAnalyzeItem{
1✔
184
                                Name: entry.Item.Name,
1✔
185
                                Type: entry.Item.Type,
1✔
186
                        }
1✔
187
                }
1✔
188
                violation := packageAnalyzeViolation{
1✔
189
                        ErrorCode:           entry.ErrorCode,
1✔
190
                        Description:         entry.Description,
1✔
191
                        RuleName:            entry.RuleName,
1✔
192
                        FilePath:            entry.FilePath,
1✔
193
                        ActivityDisplayName: entry.ActivityDisplayName,
1✔
194
                        WorkflowDisplayName: entry.WorkflowDisplayName,
1✔
195
                        ErrorSeverity:       entry.ErrorSeverity,
1✔
196
                        Recommendation:      entry.Recommendation,
1✔
197
                        DocumentationLink:   entry.DocumentationLink,
1✔
198
                        ActivityId:          activityId,
1✔
199
                        Item:                item,
1✔
200
                }
1✔
201
                violations = append(violations, violation)
1✔
202
        }
203
        return violations
1✔
204
}
205

206
func (c PackageAnalyzeCommand) wait(cmd utils.ExecCmd, wg *sync.WaitGroup) {
1✔
207
        defer wg.Done()
1✔
208
        _ = cmd.Wait()
1✔
209
}
1✔
210

211
func (c PackageAnalyzeCommand) newAnalyzingProgressBar(logger log.Logger) chan struct{} {
1✔
212
        progressBar := utils.NewProgressBar(logger)
1✔
213
        ticker := time.NewTicker(10 * time.Millisecond)
1✔
214
        cancel := make(chan struct{})
1✔
215
        go func() {
2✔
216
                for {
2✔
217
                        select {
1✔
218
                        case <-ticker.C:
1✔
219
                                progressBar.Tick("analyzing...  ")
1✔
220
                        case <-cancel:
1✔
221
                                ticker.Stop()
1✔
222
                                progressBar.Remove()
1✔
223
                                return
1✔
224
                        }
225
                }
226
        }()
227
        return cancel
1✔
228
}
229

230
func (c PackageAnalyzeCommand) getSource(context plugin.ExecutionContext) (string, error) {
1✔
231
        source := c.getParameter("source", context.Parameters)
1✔
232
        if source == "" {
1✔
233
                return "", errors.New("source is not set")
×
234
        }
×
235
        source, _ = filepath.Abs(source)
1✔
236
        fileInfo, err := os.Stat(source)
1✔
237
        if err != nil {
1✔
238
                return "", fmt.Errorf("%s not found", defaultProjectJson)
×
239
        }
×
240
        if fileInfo.IsDir() {
2✔
241
                source = filepath.Join(source, defaultProjectJson)
1✔
242
        }
1✔
243
        return source, nil
1✔
244
}
245

246
func (c PackageAnalyzeCommand) readOutput(output io.Reader, logger log.Logger, wg *sync.WaitGroup) {
1✔
247
        defer wg.Done()
1✔
248
        scanner := bufio.NewScanner(output)
1✔
249
        scanner.Split(bufio.ScanRunes)
1✔
250
        for scanner.Scan() {
2✔
251
                logger.Log(scanner.Text())
1✔
252
        }
1✔
253
}
254

255
func (c PackageAnalyzeCommand) getParameter(name string, parameters []plugin.ExecutionParameter) string {
1✔
256
        result := ""
1✔
257
        for _, p := range parameters {
2✔
258
                if p.Name == name {
2✔
259
                        if data, ok := p.Value.(string); ok {
2✔
260
                                result = data
1✔
261
                                break
1✔
262
                        }
263
                }
264
        }
265
        return result
1✔
266
}
267

268
func (c PackageAnalyzeCommand) getBoolParameter(name string, parameters []plugin.ExecutionParameter) bool {
1✔
269
        result := false
1✔
270
        for _, p := range parameters {
2✔
271
                if p.Name == name {
2✔
272
                        if data, ok := p.Value.(bool); ok {
2✔
273
                                result = data
1✔
274
                                break
1✔
275
                        }
276
                }
277
        }
278
        return result
1✔
279
}
280

281
func NewPackageAnalyzeCommand() *PackageAnalyzeCommand {
1✔
282
        return &PackageAnalyzeCommand{utils.NewExecProcess()}
1✔
283
}
1✔
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