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

UiPath / uipathcli / 13671178136

05 Mar 2025 07:47AM UTC coverage: 90.054% (-0.2%) from 90.225%
13671178136

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

```

The publish command supports the optional `--source` argument which
allows users to specify the .nupkg which should be uploaded to
orchestrator. The source argument can also be a folder and the publish
command will automatically find the latest package based on the last
modified timestamp. If the argument is not provided, it defaults to
the current execution directory.

237 of 273 new or added lines in 7 files covered. (86.81%)

3 existing lines in 2 files now uncovered.

5369 of 5962 relevant lines covered (90.05%)

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

© 2026 Coveralls, Inc