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

UiPath / uipathcli / 13651495225

04 Mar 2025 10:22AM UTC coverage: 89.903% (-0.3%) from 90.225%
13651495225

push

github

thschmitt
Add command to publish studio packages

Implemented new command `uipath studio package publish` which simplifies
uploading packages to orchestrator.

Clients can now just pack and publish with two simple commands:

```
uipath studio package pack
uipath studio package publish
```

230 of 273 new or added lines in 7 files covered. (84.25%)

1 existing line in 1 file now uncovered.

5360 of 5962 relevant lines covered (89.9%)

1.01 hits per line

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

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

3
import (
4
        "bufio"
5
        "bytes"
6
        "encoding/json"
7
        "fmt"
8
        "io"
9
        "os"
10
        "path/filepath"
11
        "slices"
12
        "strings"
13
        "sync"
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/process"
20
        "github.com/UiPath/uipathcli/utils/visualization"
21
)
22

23
const defaultProjectJson = "project.json"
24

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

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

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

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

1✔
61
        result, err := c.execute(*params, context.Debug, logger)
1✔
62
        if err != nil {
2✔
63
                return err
1✔
64
        }
1✔
65

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

73
func (c PackagePackCommand) formatAllowedValues(allowed []string) string {
1✔
74
        return "\n\nAllowed Values:\n- " + strings.Join(allowed, "\n- ")
1✔
75
}
1✔
76

77
func (c PackagePackCommand) execute(params packagePackParams, debug bool, logger log.Logger) (*packagePackResult, error) {
1✔
78
        projectReader := newStudioProjectReader(params.Source)
1✔
79
        project, err := projectReader.ReadMetadata()
1✔
80
        if err != nil {
1✔
81
                return nil, err
×
82
        }
×
83
        supported, err := project.TargetFramework.IsSupported()
1✔
84
        if !supported {
2✔
85
                return nil, err
1✔
86
        }
1✔
87
        _ = projectReader.AddToIgnoredFiles(project.NupkgIgnoreFilePattern())
1✔
88

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

95
        if !debug {
2✔
96
                bar := c.newPackagingProgressBar(logger)
1✔
97
                defer close(bar)
1✔
98
        }
1✔
99
        args := c.preparePackArguments(params)
1✔
100
        cmd, err := uipcli.Execute(args...)
1✔
101
        if err != nil {
1✔
102
                return nil, err
×
103
        }
×
104
        stdout, err := cmd.StdoutPipe()
1✔
105
        if err != nil {
1✔
106
                return nil, fmt.Errorf("Could not run pack command: %v", err)
×
107
        }
×
108
        defer stdout.Close()
1✔
109
        stderr, err := cmd.StderrPipe()
1✔
110
        if err != nil {
1✔
111
                return nil, fmt.Errorf("Could not run pack command: %v", err)
×
112
        }
×
113
        defer stderr.Close()
1✔
114
        err = cmd.Start()
1✔
115
        if err != nil {
1✔
116
                return nil, fmt.Errorf("Could not run pack command: %v", err)
×
117
        }
×
118

119
        stderrOutputBuilder := new(strings.Builder)
1✔
120
        stderrReader := io.TeeReader(stderr, stderrOutputBuilder)
1✔
121

1✔
122
        var wg sync.WaitGroup
1✔
123
        wg.Add(3)
1✔
124
        go c.readOutput(stdout, logger, &wg)
1✔
125
        go c.readOutput(stderrReader, logger, &wg)
1✔
126
        go c.wait(cmd, &wg)
1✔
127
        wg.Wait()
1✔
128

1✔
129
        exitCode := cmd.ExitCode()
1✔
130
        var result *packagePackResult
1✔
131
        if exitCode == 0 {
2✔
132
                nupkgPath := findLatestNupkg(params.Destination)
1✔
133
                nupkgReader := newNupkgReader(nupkgPath)
1✔
134
                nuspec, err := nupkgReader.ReadNuspec()
1✔
135
                if err != nil {
2✔
136
                        return nil, err
1✔
137
                }
1✔
138
                result = newSucceededPackagePackResult(
1✔
139
                        nupkgPath,
1✔
140
                        project.Name,
1✔
141
                        project.Description,
1✔
142
                        project.ProjectId,
1✔
143
                        nuspec.Version)
1✔
144
        } else {
1✔
145
                result = newFailedPackagePackResult(
1✔
146
                        stderrOutputBuilder.String(),
1✔
147
                        &project.Name,
1✔
148
                        &project.Description,
1✔
149
                        &project.ProjectId)
1✔
150
        }
1✔
151
        return result, nil
1✔
152
}
153

154
func (c PackagePackCommand) preparePackArguments(params packagePackParams) []string {
1✔
155
        args := []string{"package", "pack", params.Source, "--output", params.Destination}
1✔
156
        if params.PackageVersion != "" {
1✔
NEW
157
                args = append(args, "--version", params.PackageVersion)
×
UNCOV
158
        }
×
159
        if params.AutoVersion {
2✔
160
                args = append(args, "--autoVersion")
1✔
161
        }
1✔
162
        if params.OutputType != "" {
2✔
163
                args = append(args, "--outputType", params.OutputType)
1✔
164
        }
1✔
165
        if params.SplitOutput {
2✔
166
                args = append(args, "--splitOutput")
1✔
167
        }
1✔
168
        if params.ReleaseNotes != "" {
2✔
169
                args = append(args, "--releaseNotes", params.ReleaseNotes)
1✔
170
        }
1✔
171
        return args
1✔
172
}
173

174
func (c PackagePackCommand) wait(cmd process.ExecCmd, wg *sync.WaitGroup) {
1✔
175
        defer wg.Done()
1✔
176
        _ = cmd.Wait()
1✔
177
}
1✔
178

179
func (c PackagePackCommand) newPackagingProgressBar(logger log.Logger) chan struct{} {
1✔
180
        progressBar := visualization.NewProgressBar(logger)
1✔
181
        ticker := time.NewTicker(10 * time.Millisecond)
1✔
182
        cancel := make(chan struct{})
1✔
183
        var percent float64 = 0
1✔
184
        go func() {
2✔
185
                for {
2✔
186
                        select {
1✔
187
                        case <-ticker.C:
1✔
188
                                progressBar.UpdatePercentage("packaging...  ", percent)
1✔
189
                                percent = percent + 1
1✔
190
                                if percent > 100 {
2✔
191
                                        percent = 0
1✔
192
                                }
1✔
193
                        case <-cancel:
1✔
194
                                ticker.Stop()
1✔
195
                                progressBar.Remove()
1✔
196
                                return
1✔
197
                        }
198
                }
199
        }()
200
        return cancel
1✔
201
}
202

203
func (c PackagePackCommand) getSource(context plugin.ExecutionContext) (string, error) {
1✔
204
        source := c.getParameter("source", ".", context.Parameters)
1✔
205
        source, _ = filepath.Abs(source)
1✔
206
        fileInfo, err := os.Stat(source)
1✔
207
        if err != nil {
2✔
208
                return "", fmt.Errorf("%s not found", defaultProjectJson)
1✔
209
        }
1✔
210
        if fileInfo.IsDir() {
2✔
211
                source = filepath.Join(source, defaultProjectJson)
1✔
212
        }
1✔
213
        return source, nil
1✔
214
}
215

216
func (c PackagePackCommand) getDestination(context plugin.ExecutionContext) string {
1✔
217
        destination := c.getParameter("destination", ".", context.Parameters)
1✔
218
        destination, _ = filepath.Abs(destination)
1✔
219
        return destination
1✔
220
}
1✔
221

222
func (c PackagePackCommand) readOutput(output io.Reader, logger log.Logger, wg *sync.WaitGroup) {
1✔
223
        defer wg.Done()
1✔
224
        scanner := bufio.NewScanner(output)
1✔
225
        scanner.Split(bufio.ScanRunes)
1✔
226
        for scanner.Scan() {
2✔
227
                logger.Log(scanner.Text())
1✔
228
        }
1✔
229
}
230

231
func (c PackagePackCommand) getParameter(name string, defaultValue string, parameters []plugin.ExecutionParameter) string {
1✔
232
        result := defaultValue
1✔
233
        for _, p := range parameters {
2✔
234
                if p.Name == name {
2✔
235
                        if data, ok := p.Value.(string); ok {
2✔
236
                                result = data
1✔
237
                                break
1✔
238
                        }
239
                }
240
        }
241
        return result
1✔
242
}
243

244
func (c PackagePackCommand) getBoolParameter(name string, parameters []plugin.ExecutionParameter) bool {
1✔
245
        result := false
1✔
246
        for _, p := range parameters {
2✔
247
                if p.Name == name {
2✔
248
                        if data, ok := p.Value.(bool); ok {
2✔
249
                                result = data
1✔
250
                                break
1✔
251
                        }
252
                }
253
        }
254
        return result
1✔
255
}
256

257
func NewPackagePackCommand() *PackagePackCommand {
1✔
258
        return &PackagePackCommand{process.NewExecProcess()}
1✔
259
}
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