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

UiPath / uipathcli / 13244383932

10 Feb 2025 03:26PM UTC coverage: 90.417% (-0.03%) from 90.449%
13244383932

Pull #144

github

thschmitt
Show progress bar for downloading plugin tools

The downloading and packaging progress bars were overlapping during the
initialization of the external plugin. Split up the initialization from
the execution to show a separate progress bar for each operation.
Pull Request #144: Show progress bar for downloading plugin tools

26 of 33 new or added lines in 3 files covered. (78.79%)

1 existing line in 1 file now uncovered.

4991 of 5520 relevant lines covered (90.42%)

1.01 hits per line

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

88.79
/plugin/studio/package_pack_command.go
1
package studio
2

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

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

24
const defaultProjectJson = "project.json"
25

26
var OutputTypeAllowedValues = []string{"Process", "Library", "Tests", "Objects"}
27

28
// The PackagePackCommand packs a project into a single NuGet package
29
type PackagePackCommand struct {
30
        Exec process.ExecProcess
31
}
32

33
func (c PackagePackCommand) Command() plugin.Command {
1✔
34
        return *plugin.NewCommand("studio").
1✔
35
                WithCategory("package", "Package", "UiPath Studio package-related actions").
1✔
36
                WithOperation("pack", "Package Project", "Packs a project into a single package").
1✔
37
                WithParameter("source", plugin.ParameterTypeString, "Path to a project.json file or a folder containing project.json file", true).
1✔
38
                WithParameter("destination", plugin.ParameterTypeString, "The output folder", true).
1✔
39
                WithParameter("package-version", plugin.ParameterTypeString, "The package version", false).
1✔
40
                WithParameter("auto-version", plugin.ParameterTypeBoolean, "Auto-generate package version", false).
1✔
41
                WithParameter("output-type", plugin.ParameterTypeString, "Force the output to a specific type."+c.formatAllowedValues(OutputTypeAllowedValues), false).
1✔
42
                WithParameter("split-output", plugin.ParameterTypeBoolean, "Enables the output split to runtime and design libraries", false).
1✔
43
                WithParameter("release-notes", plugin.ParameterTypeString, "Add release notes", false)
1✔
44
}
1✔
45

46
func (c PackagePackCommand) Execute(context plugin.ExecutionContext, writer output.OutputWriter, logger log.Logger) error {
1✔
47
        source, err := c.getSource(context)
1✔
48
        if err != nil {
2✔
49
                return err
1✔
50
        }
1✔
51
        destination, err := c.getDestination(context)
1✔
52
        if err != nil {
1✔
53
                return err
×
54
        }
×
55
        packageVersion := c.getParameter("package-version", context.Parameters)
1✔
56
        autoVersion := c.getBoolParameter("auto-version", context.Parameters)
1✔
57
        outputType := c.getParameter("output-type", context.Parameters)
1✔
58
        if outputType != "" && !slices.Contains(OutputTypeAllowedValues, outputType) {
2✔
59
                return fmt.Errorf("Invalid output type '%s', allowed values: %s", outputType, strings.Join(OutputTypeAllowedValues, ", "))
1✔
60
        }
1✔
61
        splitOutput := c.getBoolParameter("split-output", context.Parameters)
1✔
62
        releaseNotes := c.getParameter("release-notes", context.Parameters)
1✔
63
        params := newPackagePackParams(source, destination, packageVersion, autoVersion, outputType, splitOutput, releaseNotes)
1✔
64

1✔
65
        result, err := c.execute(*params, context.Debug, logger)
1✔
66
        if err != nil {
1✔
67
                return err
×
68
        }
×
69

70
        json, err := json.Marshal(result)
1✔
71
        if err != nil {
1✔
72
                return fmt.Errorf("pack command failed: %v", err)
×
73
        }
×
74
        return writer.WriteResponse(*output.NewResponseInfo(200, "200 OK", "HTTP/1.1", map[string][]string{}, bytes.NewReader(json)))
1✔
75
}
76

77
func (c PackagePackCommand) formatAllowedValues(allowed []string) string {
1✔
78
        return "\n\nAllowed Values:\n- " + strings.Join(allowed, "\n- ")
1✔
79
}
1✔
80

81
func (c PackagePackCommand) execute(params packagePackParams, debug bool, logger log.Logger) (*packagePackResult, error) {
1✔
82
        args := []string{"package", "pack", params.Source, "--output", params.Destination}
1✔
83
        if params.PackageVersion != "" {
1✔
84
                args = append(args, "--version", params.PackageVersion)
×
85
        }
×
86
        if params.AutoVersion {
2✔
87
                args = append(args, "--autoVersion")
1✔
88
        }
1✔
89
        if params.OutputType != "" {
2✔
90
                args = append(args, "--outputType", params.OutputType)
1✔
91
        }
1✔
92
        if params.SplitOutput {
2✔
93
                args = append(args, "--splitOutput")
1✔
94
        }
1✔
95
        if params.ReleaseNotes != "" {
2✔
96
                args = append(args, "--releaseNotes", params.ReleaseNotes)
1✔
97
        }
1✔
98

99
        projectReader := newStudioProjectReader(params.Source)
1✔
100

1✔
101
        uipcli := newUipcli(c.Exec, logger)
1✔
102
        err := uipcli.Initialize(projectReader.GetTargetFramework())
1✔
103
        if err != nil {
1✔
NEW
104
                return nil, err
×
NEW
105
        }
×
106

107
        if !debug {
2✔
108
                bar := c.newPackagingProgressBar(logger)
1✔
109
                defer close(bar)
1✔
110
        }
1✔
111
        cmd, err := uipcli.Execute(args...)
1✔
112
        if err != nil {
1✔
113
                return nil, err
×
114
        }
×
115
        stdout, err := cmd.StdoutPipe()
1✔
116
        if err != nil {
1✔
117
                return nil, fmt.Errorf("Could not run pack command: %v", err)
×
118
        }
×
119
        defer stdout.Close()
1✔
120
        stderr, err := cmd.StderrPipe()
1✔
121
        if err != nil {
1✔
122
                return nil, fmt.Errorf("Could not run pack command: %v", err)
×
123
        }
×
124
        defer stderr.Close()
1✔
125
        err = cmd.Start()
1✔
126
        if err != nil {
1✔
127
                return nil, fmt.Errorf("Could not run pack command: %v", err)
×
128
        }
×
129

130
        stderrOutputBuilder := new(strings.Builder)
1✔
131
        stderrReader := io.TeeReader(stderr, stderrOutputBuilder)
1✔
132

1✔
133
        var wg sync.WaitGroup
1✔
134
        wg.Add(3)
1✔
135
        go c.readOutput(stdout, logger, &wg)
1✔
136
        go c.readOutput(stderrReader, logger, &wg)
1✔
137
        go c.wait(cmd, &wg)
1✔
138
        wg.Wait()
1✔
139

1✔
140
        project, err := projectReader.ReadMetadata()
1✔
141
        if err != nil {
1✔
142
                return nil, err
×
143
        }
×
144

145
        exitCode := cmd.ExitCode()
1✔
146
        var result *packagePackResult
1✔
147
        if exitCode == 0 {
2✔
148
                nupkgFile := c.findNupkg(params.Destination)
1✔
149
                version := c.extractVersion(nupkgFile)
1✔
150
                result = newSucceededPackagePackResult(
1✔
151
                        filepath.Join(params.Destination, nupkgFile),
1✔
152
                        project.Name,
1✔
153
                        project.Description,
1✔
154
                        project.ProjectId,
1✔
155
                        version)
1✔
156
        } else {
2✔
157
                result = newFailedPackagePackResult(
1✔
158
                        stderrOutputBuilder.String(),
1✔
159
                        &project.Name,
1✔
160
                        &project.Description,
1✔
161
                        &project.ProjectId)
1✔
162
        }
1✔
163
        return result, nil
1✔
164
}
165

166
func (c PackagePackCommand) findNupkg(destination string) string {
1✔
167
        newestFile := ""
1✔
168
        newestTime := time.Time{}
1✔
169

1✔
170
        files, _ := os.ReadDir(destination)
1✔
171
        for _, file := range files {
2✔
172
                extension := filepath.Ext(file.Name())
1✔
173
                if strings.EqualFold(extension, ".nupkg") {
2✔
174
                        fileInfo, _ := file.Info()
1✔
175
                        time := fileInfo.ModTime()
1✔
176
                        if time.After(newestTime) {
2✔
177
                                newestTime = time
1✔
178
                                newestFile = file.Name()
1✔
179
                        }
1✔
180
                }
181
        }
182
        return newestFile
1✔
183
}
184

185
func (c PackagePackCommand) extractVersion(nupkgFile string) string {
1✔
186
        parts := strings.Split(nupkgFile, ".")
1✔
187
        len := len(parts)
1✔
188
        if len < 4 {
2✔
189
                return ""
1✔
190
        }
1✔
191
        return fmt.Sprintf("%s.%s.%s", parts[len-4], parts[len-3], parts[len-2])
1✔
192
}
193

194
func (c PackagePackCommand) wait(cmd process.ExecCmd, wg *sync.WaitGroup) {
1✔
195
        defer wg.Done()
1✔
196
        _ = cmd.Wait()
1✔
197
}
1✔
198

199
func (c PackagePackCommand) newPackagingProgressBar(logger log.Logger) chan struct{} {
1✔
200
        progressBar := visualization.NewProgressBar(logger)
1✔
201
        ticker := time.NewTicker(10 * time.Millisecond)
1✔
202
        cancel := make(chan struct{})
1✔
203
        var percent float64 = 0
1✔
204
        go func() {
2✔
205
                for {
2✔
206
                        select {
1✔
207
                        case <-ticker.C:
1✔
208
                                progressBar.UpdatePercentage("packaging...  ", percent)
1✔
209
                                percent = percent + 1
1✔
210
                                if percent > 100 {
2✔
211
                                        percent = 0
1✔
212
                                }
1✔
213
                        case <-cancel:
1✔
214
                                ticker.Stop()
1✔
215
                                progressBar.Remove()
1✔
216
                                return
1✔
217
                        }
218
                }
219
        }()
220
        return cancel
1✔
221
}
222

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

239
func (c PackagePackCommand) getDestination(context plugin.ExecutionContext) (string, error) {
1✔
240
        destination := c.getParameter("destination", context.Parameters)
1✔
241
        if destination == "" {
1✔
242
                return "", errors.New("destination is not set")
×
243
        }
×
244
        destination, _ = filepath.Abs(destination)
1✔
245
        return destination, nil
1✔
246
}
247

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

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

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

283
func NewPackagePackCommand() *PackagePackCommand {
1✔
284
        return &PackagePackCommand{process.NewExecProcess()}
1✔
285
}
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