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

UiPath / uipathcli / 22833181054

09 Mar 2026 12:15AM UTC coverage: 90.831% (-0.08%) from 90.914%
22833181054

Pull #212

github

Chibi Vikram
Add direct Execute tests for defensive parameter validation

Test parameter type assertion failure and empty value checks by calling
Execute directly, bypassing CLI framework validation. Covers
getStringParameter false branch and source/solutionId empty checks in
unpack, push, pull, and publish commands.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pull Request #212: Add studio solution commands, skill, and agents.md

606 of 674 new or added lines in 16 files covered. (89.91%)

3 existing lines in 1 file now uncovered.

7380 of 8125 relevant lines covered (90.83%)

1.02 hits per line

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

83.56
/plugin/studio/solution/pack/solution_pack_command.go
1
// Package pack implements the command plugin for packing a UiPath solution
2
// directory into a .uis file (ZIP archive).
3
package pack
4

5
import (
6
        "archive/zip"
7
        "bytes"
8
        "encoding/json"
9
        "fmt"
10
        "io"
11
        "net/http"
12
        "os"
13
        "path/filepath"
14
        "strings"
15

16
        "github.com/UiPath/uipathcli/log"
17
        "github.com/UiPath/uipathcli/output"
18
        "github.com/UiPath/uipathcli/plugin"
19
)
20

21
// The SolutionPackCommand packs a solution directory into a .uis file.
22
type SolutionPackCommand struct{}
23

24
func (c SolutionPackCommand) Command() plugin.Command {
1✔
25
        return *plugin.NewCommand("studio").
1✔
26
                WithCategory("solution", "UiPath Solution management", "Pack, unpack, push and pull UiPath Maestro solutions.").
1✔
27
                WithOperation("pack", "Pack Solution", "Packs a solution directory into a .uis file").
1✔
28
                WithParameter(plugin.NewParameter("source", plugin.ParameterTypeString, "Path to solution directory").
1✔
29
                        WithRequired(true).
1✔
30
                        WithDefaultValue(".")).
1✔
31
                WithParameter(plugin.NewParameter("destination", plugin.ParameterTypeString, "Output .uis file path").
1✔
32
                        WithDefaultValue(""))
1✔
33
}
1✔
34

35
func (c SolutionPackCommand) Execute(ctx plugin.ExecutionContext, writer output.OutputWriter, logger log.Logger) error {
1✔
36
        source := c.getStringParameter("source", ".", ctx.Parameters)
1✔
37
        source, _ = filepath.Abs(source)
1✔
38
        destination := c.getStringParameter("destination", "", ctx.Parameters)
1✔
39

1✔
40
        fileInfo, err := os.Stat(source)
1✔
41
        if err != nil {
2✔
42
                return fmt.Errorf("Solution directory not found: %s", source)
1✔
43
        }
1✔
44
        if !fileInfo.IsDir() {
2✔
45
                return fmt.Errorf("Source is not a directory: %s", source)
1✔
46
        }
1✔
47

48
        solutionStoragePath := filepath.Join(source, "SolutionStorage.json")
1✔
49
        if _, err := os.Stat(solutionStoragePath); err != nil {
2✔
50
                return fmt.Errorf("SolutionStorage.json not found in %s. This does not appear to be a valid UiPath solution directory", source)
1✔
51
        }
1✔
52

53
        if destination == "" {
2✔
54
                destination = filepath.Base(source) + ".uis"
1✔
55
        }
1✔
56
        destination, _ = filepath.Abs(destination)
1✔
57

1✔
58
        solutionId, solutionName := c.readSolutionInfo(solutionStoragePath)
1✔
59

1✔
60
        params := newSolutionPackParams(source, destination, solutionId, solutionName)
1✔
61
        result, err := c.pack(*params)
1✔
62
        if err != nil {
2✔
63
                return err
1✔
64
        }
1✔
65

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

73
func (c SolutionPackCommand) pack(params solutionPackParams) (*solutionPackResult, error) {
1✔
74
        outFile, err := os.Create(params.Destination)
1✔
75
        if err != nil {
2✔
76
                return nil, fmt.Errorf("Cannot create output file: %w", err)
1✔
77
        }
1✔
78
        defer func() { _ = outFile.Close() }()
2✔
79

80
        zipWriter := zip.NewWriter(outFile)
1✔
81
        defer func() { _ = zipWriter.Close() }()
2✔
82

83
        err = filepath.Walk(params.Source, func(path string, info os.FileInfo, err error) error {
2✔
84
                if err != nil {
1✔
NEW
85
                        return err
×
NEW
86
                }
×
87
                relPath, err := filepath.Rel(params.Source, path)
1✔
88
                if err != nil {
1✔
NEW
89
                        return err
×
NEW
90
                }
×
91
                if c.shouldSkip(relPath, info) {
2✔
92
                        if info.IsDir() {
2✔
93
                                return filepath.SkipDir
1✔
94
                        }
1✔
95
                        return nil
1✔
96
                }
97
                if info.IsDir() {
2✔
98
                        return nil
1✔
99
                }
1✔
100
                return c.addFileToZip(zipWriter, path, relPath)
1✔
101
        })
102
        if err != nil {
2✔
103
                _ = zipWriter.Close()
1✔
104
                _ = outFile.Close()
1✔
105
                _ = os.Remove(params.Destination)
1✔
106
                return nil, err
1✔
107
        }
1✔
108

NEW
109
        // Close zip writer and file before reading size to ensure data is flushed
×
110
        _ = zipWriter.Close()
1✔
111
        _ = outFile.Close()
1✔
112

1✔
113
        fileInfo, err := os.Stat(params.Destination)
1✔
114
        size := int64(0)
1✔
115
        if err == nil {
2✔
116
                size = fileInfo.Size()
1✔
117
        }
1✔
NEW
118

×
119
        return newSucceededSolutionPackResult(params.Destination, params.SolutionId, params.SolutionName, size), nil
1✔
NEW
120
}
×
NEW
121

×
122
func (c SolutionPackCommand) shouldSkip(relPath string, info os.FileInfo) bool {
1✔
123
        if relPath == ".git" || strings.HasPrefix(relPath, ".git"+string(filepath.Separator)) {
2✔
124
                return true
1✔
125
        }
1✔
126
        if info.IsDir() && info.Name() == "__pycache__" {
2✔
127
                return true
1✔
128
        }
1✔
129
        if !info.IsDir() && strings.HasSuffix(info.Name(), ".pyc") {
2✔
130
                return true
1✔
131
        }
1✔
132
        return false
1✔
NEW
133
}
×
NEW
134

×
135
func (c SolutionPackCommand) addFileToZip(zipWriter *zip.Writer, path string, relPath string) error {
1✔
136
        zipPath := strings.ReplaceAll(relPath, string(filepath.Separator), "/")
1✔
137
        w, err := zipWriter.Create(zipPath)
1✔
138
        if err != nil {
1✔
NEW
139
                return fmt.Errorf("Error creating zip entry '%s': %w", zipPath, err)
×
NEW
140
        }
×
141
        f, err := os.Open(path)
1✔
142
        if err != nil {
2✔
143
                return fmt.Errorf("Error opening file '%s': %w", path, err)
1✔
144
        }
1✔
145
        defer func() { _ = f.Close() }()
2✔
146
        _, err = io.Copy(w, f)
1✔
147
        if err != nil {
1✔
NEW
148
                return fmt.Errorf("Error writing file '%s': %w", zipPath, err)
×
NEW
149
        }
×
150
        return nil
1✔
NEW
151
}
×
NEW
152

×
153
func (c SolutionPackCommand) readSolutionInfo(path string) (string, string) {
1✔
154
        data, err := os.ReadFile(path)
1✔
155
        if err != nil {
1✔
NEW
156
                return "", ""
×
NEW
157
        }
×
158
        var storage struct {
1✔
159
                SolutionId string `json:"SolutionId"`
1✔
160
        }
1✔
161
        err = json.Unmarshal(data, &storage)
1✔
162
        if err == nil {
2✔
163
                return storage.SolutionId, ""
1✔
164
        }
1✔
165
        return "", ""
1✔
NEW
166
}
×
NEW
167

×
168
func (c SolutionPackCommand) getStringParameter(name string, defaultValue string, parameters []plugin.ExecutionParameter) string {
1✔
169
        result := defaultValue
1✔
170
        for _, p := range parameters {
2✔
171
                if p.Name == name {
2✔
172
                        if data, ok := p.Value.(string); ok {
2✔
173
                                result = data
1✔
174
                                break
1✔
175
                        }
176
                }
NEW
177
        }
×
178
        return result
1✔
NEW
179
}
×
180

181
func NewSolutionPackCommand() *SolutionPackCommand {
1✔
182
        return &SolutionPackCommand{}
1✔
183
}
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