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

UiPath / uipathcli / 12390549287

18 Dec 2024 09:56AM UTC coverage: 86.121% (+0.05%) from 86.074%
12390549287

push

github

thschmitt
Add support to analyze studio projects

56 of 272 new or added lines in 8 files covered. (20.59%)

70 existing lines in 4 files now uncovered.

4660 of 5411 relevant lines covered (86.12%)

0.97 hits per line

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

5.26
/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

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

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

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

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

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

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

NEW
105
        stderrOutputBuilder := new(strings.Builder)
×
NEW
106
        stderrReader := io.TeeReader(stderr, stderrOutputBuilder)
×
NEW
107

×
NEW
108
        var wg sync.WaitGroup
×
NEW
109
        wg.Add(3)
×
NEW
110
        go c.readOutput(stdout, logger, &wg)
×
NEW
111
        go c.readOutput(stderrReader, logger, &wg)
×
NEW
112
        go c.wait(cmd, &wg)
×
NEW
113
        wg.Wait()
×
NEW
114

×
NEW
115
        violations, err := c.readAnalyzeResult(jsonResultFilePath)
×
NEW
116
        if err != nil {
×
NEW
117
                return 1, nil, err
×
NEW
118
        }
×
119

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

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

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

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

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

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

NEW
203
func (c PackageAnalyzeCommand) wait(cmd utils.ExecCmd, wg *sync.WaitGroup) {
×
NEW
204
        defer wg.Done()
×
NEW
205
        _ = cmd.Wait()
×
NEW
206
}
×
207

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

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

NEW
242
func (c PackageAnalyzeCommand) readOutput(output io.Reader, logger log.Logger, wg *sync.WaitGroup) {
×
NEW
243
        defer wg.Done()
×
NEW
244
        scanner := bufio.NewScanner(output)
×
NEW
245
        scanner.Split(bufio.ScanRunes)
×
NEW
246
        for scanner.Scan() {
×
NEW
247
                logger.Log(scanner.Text())
×
NEW
248
        }
×
249
}
250

NEW
251
func (c PackageAnalyzeCommand) getParameter(name string, parameters []plugin.ExecutionParameter) (string, error) {
×
NEW
252
        for _, p := range parameters {
×
NEW
253
                if p.Name == name {
×
NEW
254
                        if data, ok := p.Value.(string); ok {
×
NEW
255
                                return data, nil
×
NEW
256
                        }
×
257
                }
258
        }
NEW
259
        return "", fmt.Errorf("Could not find '%s' parameter", name)
×
260
}
261

NEW
262
func (c PackageAnalyzeCommand) getBoolParameter(name string, parameters []plugin.ExecutionParameter) (bool, error) {
×
NEW
263
        for _, p := range parameters {
×
NEW
264
                if p.Name == name {
×
NEW
265
                        if data, ok := p.Value.(bool); ok {
×
NEW
266
                                return data, nil
×
NEW
267
                        }
×
268
                }
269
        }
NEW
270
        return false, fmt.Errorf("Could not find '%s' parameter", name)
×
271
}
272

273
func NewPackageAnalyzeCommand() *PackageAnalyzeCommand {
1✔
274
        return &PackageAnalyzeCommand{utils.NewExecProcess()}
1✔
275
}
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

© 2025 Coveralls, Inc