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

UiPath / uipathcli / 13287131513

12 Feb 2025 02:01PM UTC coverage: 90.08% (-0.4%) from 90.522%
13287131513

push

github

thschmitt
Download .NET runtime for cross-platform Studio Projects

- Downloading the required .NET runtime when the user builds
  cross-platform Studio projects which allows the uipathcli to work
  without any additional dependencies

- Implemented support to for tar.gz archives which is needed to extract
  dotnet runtime archives on Linux and MacOS

- Added retry mechanism for downloading the plugins to make the
  operation more robust

97 of 134 new or added lines in 5 files covered. (72.39%)

2 existing lines in 1 file now uncovered.

5085 of 5645 relevant lines covered (90.08%)

1.01 hits per line

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

74.74
/plugin/external_plugin.go
1
package plugin
2

3
import (
4
        "crypto/rand"
5
        "crypto/sha256"
6
        "fmt"
7
        "io"
8
        "math"
9
        "math/big"
10
        "net/http"
11
        "os"
12
        "path/filepath"
13

14
        "github.com/UiPath/uipathcli/log"
15
        "github.com/UiPath/uipathcli/utils/directories"
16
        "github.com/UiPath/uipathcli/utils/resiliency"
17
        "github.com/UiPath/uipathcli/utils/visualization"
18
)
19

20
const pluginDirectoryPermissions = 0700
21

22
type ExternalPlugin struct {
23
        Logger log.Logger
24
}
25

26
func (p ExternalPlugin) GetTool(name string, url string, archiveType ArchiveType, executable string) (string, error) {
1✔
27
        path := ""
1✔
28
        err := resiliency.Retry(func() error {
2✔
29
                var err error
1✔
30
                path, err = p.getTool(name, url, archiveType, executable)
1✔
31
                if err != nil {
1✔
NEW
32
                        return resiliency.Retryable(err)
×
NEW
33
                }
×
34
                return nil
1✔
35
        })
36
        return path, err
1✔
37
}
38

39
func (p ExternalPlugin) getTool(name string, url string, archiveType ArchiveType, executable string) (string, error) {
1✔
40
        pluginDirectory, err := p.pluginDirectory(name, url)
1✔
41
        if err != nil {
1✔
42
                return "", fmt.Errorf("Could not download %s: %v", name, err)
×
43
        }
×
44
        path := filepath.Join(pluginDirectory, executable)
1✔
45
        if _, err := os.Stat(path); err == nil {
2✔
46
                return path, nil
1✔
47
        }
1✔
48

49
        tmpPluginDirectory := pluginDirectory + "-" + p.randomFolderName()
1✔
50
        _ = os.MkdirAll(tmpPluginDirectory, pluginDirectoryPermissions)
1✔
51

1✔
52
        progressBar := visualization.NewProgressBar(p.Logger)
1✔
53
        defer progressBar.Remove()
1✔
54
        progressBar.UpdatePercentage("downloading...", 0)
1✔
55
        archivePath := filepath.Join(tmpPluginDirectory, name)
1✔
56
        err = p.download(name, url, archivePath, progressBar)
1✔
57
        if err != nil {
1✔
58
                return "", err
×
59
        }
×
60
        archive := newArchive(archiveType)
1✔
61
        err = archive.Extract(archivePath, tmpPluginDirectory, pluginDirectoryPermissions)
1✔
62
        if err != nil {
1✔
63
                return "", fmt.Errorf("Could not extract %s archive: %v", name, err)
×
64
        }
×
65
        err = os.Remove(archivePath)
1✔
66
        if err != nil {
1✔
NEW
67
                return "", fmt.Errorf("Could not remove %s archive: %v", name, err)
×
NEW
68
        }
×
69
        err = p.rename(tmpPluginDirectory, pluginDirectory)
1✔
70
        if err != nil {
1✔
71
                return "", fmt.Errorf("Could not install %s: %v", name, err)
×
72
        }
×
73
        return path, nil
1✔
74
}
75

76
func (p ExternalPlugin) rename(source string, target string) error {
1✔
77
        return resiliency.RetryN(10, func() error {
2✔
78
                err := os.Rename(source, target)
1✔
79
                if err != nil {
1✔
NEW
80
                        return resiliency.Retryable(err)
×
NEW
81
                }
×
82
                return nil
1✔
83
        })
84
}
85

86
func (p ExternalPlugin) download(name string, url string, destination string, progressBar *visualization.ProgressBar) error {
1✔
87
        out, err := os.Create(destination)
1✔
88
        if err != nil {
1✔
89
                return fmt.Errorf("Could not download %s: %v", name, err)
×
90
        }
×
91
        defer out.Close()
1✔
92

1✔
93
        request, err := http.NewRequest(http.MethodGet, url, nil)
1✔
94
        if err != nil {
1✔
95
                return fmt.Errorf("Could not download %s: %v", name, err)
×
96
        }
×
97
        response, err := http.DefaultClient.Do(request)
1✔
98
        if err != nil {
1✔
99
                return fmt.Errorf("Could not download %s: %v", name, err)
×
100
        }
×
101
        downloadReader := p.progressReader("downloading...", "installing... ", response.Body, response.ContentLength, progressBar)
1✔
102
        _, err = io.Copy(out, downloadReader)
1✔
103
        if err != nil {
1✔
104
                return fmt.Errorf("Could not download %s: %v", name, err)
×
105
        }
×
106
        return nil
1✔
107
}
108

109
func (p ExternalPlugin) progressReader(text string, completedText string, reader io.Reader, length int64, progressBar *visualization.ProgressBar) io.Reader {
1✔
110
        progressReader := visualization.NewProgressReader(reader, func(progress visualization.Progress) {
2✔
111
                displayText := text
1✔
112
                if progress.Completed {
2✔
113
                        displayText = completedText
1✔
114
                }
1✔
115
                progressBar.UpdateProgress(displayText, progress.BytesRead, length, progress.BytesPerSecond)
1✔
116
        })
117
        return progressReader
1✔
118
}
119

120
func (p ExternalPlugin) pluginDirectory(name string, url string) (string, error) {
1✔
121
        pluginDirectory, err := directories.Plugin()
1✔
122
        if err != nil {
1✔
123
                return "", err
×
124
        }
×
125
        hash := sha256.Sum256([]byte(url))
1✔
126
        subdirectory := fmt.Sprintf("%s-%x", name, hash)
1✔
127
        return filepath.Join(pluginDirectory, subdirectory), nil
1✔
128
}
129

130
func (p ExternalPlugin) randomFolderName() string {
1✔
131
        value, _ := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
1✔
132
        return value.String()
1✔
133
}
1✔
134

135
func NewExternalPlugin(logger log.Logger) *ExternalPlugin {
1✔
136
        return &ExternalPlugin{logger}
1✔
137
}
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