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

UiPath / uipathcli / 14464429858

15 Apr 2025 08:06AM UTC coverage: 90.096% (+0.1%) from 89.947%
14464429858

push

github

web-flow
Merge pull request #174 from UiPath/fix/library-auth-identity-url

Pass identity url when authenticating with external libraries

44 of 44 new or added lines in 7 files covered. (100.0%)

4 existing lines in 2 files now uncovered.

6550 of 7270 relevant lines covered (90.1%)

1.01 hits per line

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

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

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

16
        "github.com/UiPath/uipathcli/log"
17
        "github.com/UiPath/uipathcli/output"
18
        "github.com/UiPath/uipathcli/plugin"
19
        "github.com/UiPath/uipathcli/utils/directories"
20
        "github.com/UiPath/uipathcli/utils/process"
21
        "github.com/UiPath/uipathcli/utils/visualization"
22
)
23

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

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

39
func (c PackageAnalyzeCommand) Execute(ctx plugin.ExecutionContext, writer output.OutputWriter, logger log.Logger) error {
1✔
40
        source, err := c.getSource(ctx)
1✔
41
        if err != nil {
2✔
42
                return err
1✔
43
        }
1✔
44

45
        stopOnRuleViolation := c.getBoolParameter("stop-on-rule-violation", true, ctx.Parameters)
1✔
46
        treatWarningsAsErrors := c.getBoolParameter("treat-warnings-as-errors", false, ctx.Parameters)
1✔
47
        governanceFile, err := c.getGovernanceFile(ctx, source)
1✔
48
        if err != nil {
2✔
49
                return err
1✔
50
        }
1✔
51

52
        exitCode, result, err := c.execute(source, stopOnRuleViolation, treatWarningsAsErrors, governanceFile, ctx.Debug, logger)
1✔
53
        if err != nil {
2✔
54
                return err
1✔
55
        }
1✔
56

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

71
func (c PackageAnalyzeCommand) execute(source string, stopOnRuleViolation bool, treatWarningsAsErrors bool, governanceFile string, debug bool, logger log.Logger) (int, *packageAnalyzeResult, error) {
1✔
72
        jsonResultFilePath, err := c.getTemporaryJsonResultFilePath()
1✔
73
        if err != nil {
1✔
74
                return 1, nil, err
×
75
        }
×
76
        defer os.Remove(jsonResultFilePath)
1✔
77

1✔
78
        projectReader := newStudioProjectReader(source)
1✔
79
        project, err := projectReader.ReadMetadata()
1✔
80
        if err != nil {
1✔
81
                return 1, nil, err
×
82
        }
×
83
        supported, err := project.TargetFramework.IsSupported()
1✔
84
        if !supported {
2✔
85
                return 1, nil, err
1✔
86
        }
1✔
87

88
        uipcli := newUipcli(c.Exec, logger)
1✔
89
        err = uipcli.Initialize(project.TargetFramework)
1✔
90
        if err != nil {
1✔
91
                return 1, nil, err
×
92
        }
×
93

94
        if !debug {
2✔
95
                bar := c.newAnalyzingProgressBar(logger)
1✔
96
                defer close(bar)
1✔
97
        }
1✔
98
        args := c.prepareAnalyzeArguments(source, jsonResultFilePath, governanceFile)
1✔
99
        exitCode, stdErr, err := uipcli.ExecuteAndWait(args...)
1✔
100
        if err != nil {
1✔
101
                return exitCode, nil, err
×
102
        }
×
103

104
        violations, err := c.readAnalyzeResult(jsonResultFilePath)
1✔
105
        if err != nil {
1✔
106
                return 1, nil, err
×
107
        }
×
108
        errorViolationsFound := c.hasErrorViolations(violations, treatWarningsAsErrors)
1✔
109

1✔
110
        if exitCode != 0 {
2✔
111
                return exitCode, newErrorPackageAnalyzeResult(violations, stdErr), nil
1✔
112
        } else if stopOnRuleViolation && errorViolationsFound {
3✔
113
                return 1, newFailedPackageAnalyzeResult(violations), nil
1✔
114
        } else if errorViolationsFound {
3✔
115
                return 0, newFailedPackageAnalyzeResult(violations), nil
1✔
116
        }
1✔
117
        return 0, newSucceededPackageAnalyzeResult(violations), nil
1✔
118
}
119

120
func (c PackageAnalyzeCommand) prepareAnalyzeArguments(source string, jsonResultFilePath string, governanceFile string) []string {
1✔
121
        args := []string{"package", "analyze", source, "--resultPath", jsonResultFilePath}
1✔
122
        if governanceFile != "" {
2✔
123
                args = append(args, "--governanceFilePath", governanceFile)
1✔
124
        }
1✔
125
        return args
1✔
126
}
127

128
func (c PackageAnalyzeCommand) hasErrorViolations(violations []packageAnalyzeViolation, treatWarningsAsErrors bool) bool {
1✔
129
        for _, violation := range violations {
2✔
130
                if violation.Severity == "Error" {
2✔
131
                        return true
1✔
132
                }
1✔
133
                if treatWarningsAsErrors && violation.Severity == "Warning" {
1✔
UNCOV
134
                        return true
×
UNCOV
135
                }
×
136
        }
137
        return false
1✔
138
}
139

140
func (c PackageAnalyzeCommand) getTemporaryJsonResultFilePath() (string, error) {
1✔
141
        tempDirectory, err := directories.Temp()
1✔
142
        if err != nil {
1✔
143
                return "", err
×
144
        }
×
145
        fileName := c.randomJsonResultFileName()
1✔
146
        return filepath.Join(tempDirectory, fileName), nil
1✔
147
}
148

149
func (c PackageAnalyzeCommand) randomJsonResultFileName() string {
1✔
150
        value, _ := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
1✔
151
        return "analyzeresult-" + value.String() + ".json"
1✔
152
}
1✔
153

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

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

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

212
func (c PackageAnalyzeCommand) convertToSeverity(errorSeverity int) string {
1✔
213
        switch errorSeverity {
1✔
214
        case 0:
×
215
                return "Off"
×
216
        case 1:
1✔
217
                return "Error"
1✔
218
        case 2:
1✔
219
                return "Warning"
1✔
220
        case 3:
1✔
221
                return "Info"
1✔
222
        default:
×
223
                return "Trace"
×
224
        }
225
}
226

227
func (c PackageAnalyzeCommand) newAnalyzingProgressBar(logger log.Logger) chan struct{} {
1✔
228
        progressBar := visualization.NewProgressBar(logger)
1✔
229
        ticker := time.NewTicker(10 * time.Millisecond)
1✔
230
        cancel := make(chan struct{})
1✔
231
        var percent float64 = 0
1✔
232
        go func() {
2✔
233
                for {
2✔
234
                        select {
1✔
235
                        case <-ticker.C:
1✔
236
                                progressBar.UpdatePercentage("analyzing...  ", percent)
1✔
237
                                percent = percent + 1
1✔
238
                                if percent > 100 {
2✔
239
                                        percent = 0
1✔
240
                                }
1✔
241
                        case <-cancel:
1✔
242
                                ticker.Stop()
1✔
243
                                progressBar.Remove()
1✔
244
                                return
1✔
245
                        }
246
                }
247
        }()
248
        return cancel
1✔
249
}
250

251
func (c PackageAnalyzeCommand) getSource(ctx plugin.ExecutionContext) (string, error) {
1✔
252
        source := c.getParameter("source", ".", ctx.Parameters)
1✔
253
        source, _ = filepath.Abs(source)
1✔
254
        fileInfo, err := os.Stat(source)
1✔
255
        if err != nil {
2✔
256
                return "", fmt.Errorf("%s not found", defaultProjectJson)
1✔
257
        }
1✔
258
        if fileInfo.IsDir() {
2✔
259
                source = filepath.Join(source, defaultProjectJson)
1✔
260
        }
1✔
261
        return source, nil
1✔
262
}
263

264
func (c PackageAnalyzeCommand) defaultGovernanceFile(source string) string {
1✔
265
        directory := filepath.Dir(source)
1✔
266
        file := filepath.Join(directory, "uipath.policy.default.json")
1✔
267
        fileInfo, err := os.Stat(file)
1✔
268
        if err != nil || fileInfo.IsDir() {
2✔
269
                return ""
1✔
270
        }
1✔
271
        return file
1✔
272
}
273

274
func (c PackageAnalyzeCommand) getGovernanceFile(context plugin.ExecutionContext, source string) (string, error) {
1✔
275
        governanceFileParam := c.getParameter("governance-file", "", context.Parameters)
1✔
276
        if governanceFileParam == "" {
2✔
277
                return c.defaultGovernanceFile(source), nil
1✔
278
        }
1✔
279

280
        file, _ := filepath.Abs(governanceFileParam)
1✔
281
        fileInfo, err := os.Stat(file)
1✔
282
        if err != nil || fileInfo.IsDir() {
2✔
283
                return "", fmt.Errorf("%s not found", governanceFileParam)
1✔
284
        }
1✔
285
        return file, nil
1✔
286
}
287

288
func (c PackageAnalyzeCommand) getParameter(name string, defaultValue string, parameters []plugin.ExecutionParameter) string {
1✔
289
        result := defaultValue
1✔
290
        for _, p := range parameters {
2✔
291
                if p.Name == name {
2✔
292
                        if data, ok := p.Value.(string); ok {
2✔
293
                                result = data
1✔
294
                                break
1✔
295
                        }
296
                }
297
        }
298
        return result
1✔
299
}
300

301
func (c PackageAnalyzeCommand) getBoolParameter(name string, defaultValue bool, parameters []plugin.ExecutionParameter) bool {
1✔
302
        result := defaultValue
1✔
303
        for _, p := range parameters {
2✔
304
                if p.Name == name {
2✔
305
                        if data, ok := p.Value.(bool); ok {
2✔
306
                                result = data
1✔
307
                                break
1✔
308
                        }
309
                }
310
        }
311
        return result
1✔
312
}
313

314
func NewPackageAnalyzeCommand() *PackageAnalyzeCommand {
1✔
315
        return &PackageAnalyzeCommand{process.NewExecProcess()}
1✔
316
}
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