Skip to content

Commit 8f6f557

Browse files
authored
Downloader helper user agent (#227)
* add global variables and Info struct in order to inject build time values via ldflags * replace cli.AppName with global variable global.GetAppName() getter * replaced cli.Version var with global getter * add .idea to .gitignore * re organize imports * add license to global.go * add hardcoded fallback version string * organize imports step2 * organize imports step3 * replace cli.AppName with global variable global.GetAppName() getter * add User-Agent header to downloader helper configuration * add testing for user agent-string generation * re organize imports * refactor global package into version package and rename and replace having cli login,logout,validate removed * refactor versioning variables in version package and implement Info struct wihth String method * refactor Info cosntructor to use directly package vars and renamed package from global to version * replace package name from global to version * solve package name clash for version and cli/version * replace application name getter with VersionInfo.Application field having removed cli login,logout,validate * search and replace version string getter with VersionInfo.VersionString field * implement empty http.Header struct propagation from cli to resources * align downloader helper to use propagated http.Header from upper layers and modify test accordingly * align daemon to use empty http.Header as struct field in ArduinoCoreServerImpl * clean inside cobra commands for empty http.Header struct and inject headers build from VersionInfo struct in cli InitInstance func * remove unused runDaemonCommand in daemon * add user agent specific for daemon mode via DownloaderHeaders property for ArduinoCoreServerImpl * add testing information in README.md * removed getters and related usage for version package * update dependencies * finalize helpers_test User-Agent header value * add integration test for version info injection via vars * tidy go mod and reorganize imports and tidy long lines * inject ldflags variables in build task in Taskfile.yml * replace commands in /travis.yml with tasks properly merging flags * fix install command for go-task to solve "undefined: interp.EnvFromList" error * add coverage files to .gitignore * tidy dependencies in go.mod * update task executable path in tasks due to install path generated by go-task install script * implement exported variable cli.HTTPClientHeader in order to have it available in all cli subpackages and not onli in cli * fix comment replacing "version config" with "global config" * leverage cli.VersionInfo to present a better terminal and json data for version info
1 parent fe92910 commit 8f6f557

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+419
-160
lines changed

Diff for: .gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55
/cmd/formatter/debug.test
66
/arduino-cli.yaml
77
/wiki
8+
.idea
9+
coverage_*.txt

Diff for: .travis.yml

+5-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ env:
1111
# Make sure golangci-lint is vendored.
1212
before_install:
1313
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.16.0
14+
- curl -sL https://taskfile.dev/install.sh | sh
1415

1516
install: true
1617

@@ -20,9 +21,10 @@ script:
2021
# Run linter
2122
- golangci-lint run
2223
# Build and test
23-
- go build
24-
- go test -timeout 20m -v -coverpkg=./... -coverprofile=coverage.txt -covermode=atomic ./...
24+
- ./bin/task build
25+
- ./bin/task test
2526

2627
after_success:
27-
- bash <(curl -s https://codecov.io/bash)
28+
- bash <(curl -s https://codecov.io/bash) -cF unittests,integration
29+
2830

Diff for: README.md

+17
Original file line numberDiff line numberDiff line change
@@ -333,3 +333,20 @@ Because:
333333
#### How can I find the core/FQBN for a board?
334334

335335
See: https://github.com/arduino/arduino-cli#step-4-find-and-install-the-right-core
336+
337+
# Testing
338+
339+
Currently Unit and Integration test are available for launch in 2 ways:
340+
341+
1. classic `go test ./...` to launch both unit and integration test
342+
343+
2. via [task](https://taskfile.dev) that includes the following options:
344+
345+
```
346+
* build: Build the project
347+
* test: Run the full testsuite
348+
* test-integration: Run integration tests only
349+
* test-unit: Run unit tests only
350+
```
351+
352+
For Example to launch unit tests only run: `task test-unit`

Diff for: Taskfile.yml

+20-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ tasks:
44
build:
55
desc: Build the project
66
cmds:
7-
- go build -v -i
7+
- go build -v -i {{.LDFLAGS}}
88

99
test:
1010
desc: Run the full testsuite
@@ -15,9 +15,26 @@ tasks:
1515
test-unit:
1616
desc: Run unit tests only
1717
cmds:
18-
- go test -short {{ default "-v" .GOFLAGS }} {{ default "./..." .TARGETS }}
18+
- go test -short {{ default "-v" .GOFLAGS }} -coverprofile=coverage_unit.txt {{ default "./..." .TARGETS }}
1919

2020
test-integration:
2121
desc: Run integration tests only
2222
cmds:
23-
- go test -run Integration {{ default "-v" .GOFLAGS }} {{ default "./..." .TARGETS }}
23+
- go test -run Integration {{ default "-v" .GOFLAGS }} -coverprofile=coverage_integ.txt {{ default "./..." .TARGETS }} {{.TEST_LDFLAGS}}
24+
25+
vars:
26+
# build vars
27+
VERSIONSTRING: "0.3.6-alpha.preview"
28+
COMMIT:
29+
sh: echo ${TRAVIS_COMMIT:-`git log -n 1 --format=%h`}
30+
LDFLAGS: >
31+
-ldflags '-X github.com/arduino/arduino-cli/version.versionString={{.VERSIONSTRING}}
32+
-X github.com/arduino/arduino-cli/version.commit={{.COMMIT}}'
33+
34+
# test vars
35+
GOFLAGS: "-timeout 5m -v -coverpkg=./... -covermode=atomic"
36+
TEST_VERSIONSTRING: "0.0.0-test.preview"
37+
TEST_COMMIT: "deadbeef"
38+
TEST_LDFLAGS: >
39+
-ldflags '-X github.com/arduino/arduino-cli/version.versionString={{.TEST_VERSIONSTRING}}
40+
-X github.com/arduino/arduino-cli/version.commit={{.TEST_COMMIT}}'

Diff for: arduino/cores/packagemanager/download.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package packagemanager
1919

2020
import (
2121
"fmt"
22+
"net/http"
2223

2324
"github.com/arduino/arduino-cli/arduino/cores"
2425
"go.bug.st/downloader"
@@ -102,16 +103,16 @@ func (pm *PackageManager) FindPlatformReleaseDependencies(item *PlatformReferenc
102103

103104
// DownloadToolRelease downloads a ToolRelease. If the tool is already downloaded a nil Downloader
104105
// is returned.
105-
func (pm *PackageManager) DownloadToolRelease(tool *cores.ToolRelease) (*downloader.Downloader, error) {
106+
func (pm *PackageManager) DownloadToolRelease(tool *cores.ToolRelease, downloaderHeaders http.Header) (*downloader.Downloader, error) {
106107
resource := tool.GetCompatibleFlavour()
107108
if resource == nil {
108109
return nil, fmt.Errorf("tool not available for your OS")
109110
}
110-
return resource.Download(pm.DownloadDir)
111+
return resource.Download(pm.DownloadDir, downloaderHeaders)
111112
}
112113

113114
// DownloadPlatformRelease downloads a PlatformRelease. If the platform is already downloaded a
114115
// nil Downloader is returned.
115-
func (pm *PackageManager) DownloadPlatformRelease(platform *cores.PlatformRelease) (*downloader.Downloader, error) {
116-
return platform.Resource.Download(pm.DownloadDir)
116+
func (pm *PackageManager) DownloadPlatformRelease(platform *cores.PlatformRelease, downloaderHeaders http.Header) (*downloader.Downloader, error) {
117+
return platform.Resource.Download(pm.DownloadDir, downloaderHeaders)
117118
}

Diff for: arduino/resources/helpers.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ package resources
1919

2020
import (
2121
"fmt"
22+
"net/http"
2223
"os"
2324

24-
paths "github.com/arduino/go-paths-helper"
25+
"github.com/arduino/go-paths-helper"
2526
"go.bug.st/downloader"
2627
)
2728

@@ -45,7 +46,7 @@ func (r *DownloadResource) IsCached(downloadDir *paths.Path) (bool, error) {
4546
}
4647

4748
// Download a DownloadResource.
48-
func (r *DownloadResource) Download(downloadDir *paths.Path) (*downloader.Downloader, error) {
49+
func (r *DownloadResource) Download(downloadDir *paths.Path, downloaderHeaders http.Header) (*downloader.Downloader, error) {
4950
cached, err := r.TestLocalArchiveIntegrity(downloadDir)
5051
if err != nil {
5152
return nil, fmt.Errorf("testing local archive integrity: %s", err)
@@ -73,5 +74,7 @@ func (r *DownloadResource) Download(downloadDir *paths.Path) (*downloader.Downlo
7374
return nil, fmt.Errorf("getting archive file info: %s", err)
7475
}
7576

76-
return downloader.Download(path.String(), r.URL)
77+
downloadConfig := downloader.Config{
78+
RequestHeaders: downloaderHeaders}
79+
return downloader.DownloadWithConfig(path.String(), r.URL, downloadConfig)
7780
}

Diff for: arduino/resources/helpers_test.go

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* This file is part of arduino-cli.
3+
*
4+
* Copyright 2018 ARDUINO SA (http://www.arduino.cc/)
5+
*
6+
* This software is released under the GNU General Public License version 3,
7+
* which covers the main part of arduino-cli.
8+
* The terms of this license can be found at:
9+
* https://www.gnu.org/licenses/gpl-3.0.en.html
10+
*
11+
* You can be released from the requirements of the above licenses by purchasing
12+
* a commercial license. Buying such a license is mandatory if you want to modify or
13+
* otherwise use the software for commercial activities involving the Arduino
14+
* software without disclosing the source code of your own applications. To purchase
15+
* a commercial license, send an email to [email protected].
16+
*/
17+
18+
package resources
19+
20+
import (
21+
"fmt"
22+
"io/ioutil"
23+
"net/http"
24+
"net/http/httptest"
25+
"strings"
26+
"testing"
27+
28+
"github.com/arduino/go-paths-helper"
29+
"github.com/stretchr/testify/require"
30+
)
31+
32+
type EchoHandler struct{}
33+
34+
// EchoHandler echos back the request as a response if used as http handler
35+
func (h *EchoHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
36+
request.Write(writer)
37+
}
38+
39+
func TestDownloadApplyUserAgentHeaderUsingConfig(t *testing.T) {
40+
goldUserAgentValue := fmt.Sprintf("arduino-cli/0.0.0-test.preview (amd64; linux; go1.12.4) Commit:deadbeef/Build:2019-06-12 11:11:11.111")
41+
goldUserAgentString := "User-Agent: " + goldUserAgentValue
42+
43+
tmp, err := paths.MkTempDir("", "")
44+
require.NoError(t, err)
45+
defer tmp.RemoveAll()
46+
47+
// startup echo server
48+
srv := httptest.NewServer(&EchoHandler{})
49+
defer srv.Close()
50+
51+
r := &DownloadResource{
52+
ArchiveFileName: "echo.txt",
53+
CachePath: "cache",
54+
URL: srv.URL,
55+
}
56+
57+
d, err := r.Download(tmp, http.Header{"User-Agent": []string{goldUserAgentValue}})
58+
require.NoError(t, err)
59+
err = d.Run()
60+
require.NoError(t, err)
61+
62+
// leverage the download helper to download the echo for the request made by the downloader itself
63+
//
64+
// expect something like:
65+
// GET /echo HTTP/1.1
66+
// Host: 127.0.0.1:64999
67+
// User-Agent: arduino-cli/0.0.0-test.preview (amd64; linux; go1.12.4) Commit:deadbeef/Build:2019-06-12 11:11:11.111
68+
// Accept-Encoding: gzip
69+
70+
b, err := ioutil.ReadFile(tmp.String() + "/cache/echo.txt") // just pass the file name
71+
require.NoError(t, err)
72+
73+
requestLines := strings.Split(string(b), "\r\n")
74+
userAgentHeaderString := ""
75+
for _, line := range requestLines {
76+
if strings.Contains(line, "User-Agent: ") {
77+
userAgentHeaderString = line
78+
}
79+
}
80+
require.Equal(t, goldUserAgentString, userAgentHeaderString)
81+
82+
}

Diff for: arduino/resources/resources_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package resources
2020
import (
2121
"crypto"
2222
"encoding/hex"
23+
"net/http"
2324
"testing"
2425

2526
paths "github.com/arduino/go-paths-helper"
@@ -43,7 +44,7 @@ func TestDownloadAndChecksums(t *testing.T) {
4344
require.NoError(t, err)
4445

4546
downloadAndTestChecksum := func() {
46-
d, err := r.Download(tmp)
47+
d, err := r.Download(tmp, http.Header{})
4748
require.NoError(t, err)
4849
err = d.Run()
4950
require.NoError(t, err)
@@ -59,7 +60,7 @@ func TestDownloadAndChecksums(t *testing.T) {
5960
downloadAndTestChecksum()
6061

6162
// Download with cached file
62-
d, err := r.Download(tmp)
63+
d, err := r.Download(tmp, http.Header{})
6364
require.NoError(t, err)
6465
require.Nil(t, d)
6566

Diff for: cli/board/attach.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ func initAttachCommand() *cobra.Command {
3333
Use: "attach <port>|<FQBN> [sketchPath]",
3434
Short: "Attaches a sketch to a board.",
3535
Long: "Attaches a sketch to a board.",
36-
Example: " " + cli.AppName + " board attach serial:///dev/tty/ACM0\n" +
37-
" " + cli.AppName + " board attach serial:///dev/tty/ACM0 HelloWorld\n" +
38-
" " + cli.AppName + " board attach arduino:samd:mkr1000",
36+
Example: " " + cli.VersionInfo.Application + " board attach serial:///dev/tty/ACM0\n" +
37+
" " + cli.VersionInfo.Application + " board attach serial:///dev/tty/ACM0 HelloWorld\n" +
38+
" " + cli.VersionInfo.Application + " board attach arduino:samd:mkr1000",
3939
Args: cobra.RangeArgs(1, 2),
4040
Run: runAttachCommand,
4141
}

Diff for: cli/board/board.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ func InitCommand() *cobra.Command {
2929
Short: "Arduino board commands.",
3030
Long: "Arduino board commands.",
3131
Example: " # Lists all connected boards.\n" +
32-
" " + cli.AppName + " board list\n\n" +
32+
" " + cli.VersionInfo.Application + " board list\n\n" +
3333
" # Attaches a sketch to a board.\n" +
34-
" " + cli.AppName + " board attach serial:///dev/tty/ACM0 mySketch",
34+
" " + cli.VersionInfo.Application + " board attach serial:///dev/tty/ACM0 mySketch",
3535
}
3636
boardCommand.AddCommand(initAttachCommand())
3737
boardCommand.AddCommand(initDetailsCommand())

Diff for: cli/board/details.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func initDetailsCommand() *cobra.Command {
3535
Use: "details <FQBN>",
3636
Short: "Print details about a board.",
3737
Long: "Show information about a board, in particular if the board has options to be specified in the FQBN.",
38-
Example: " " + cli.AppName + " board details arduino:avr:nano",
38+
Example: " " + cli.VersionInfo.Application + " board details arduino:avr:nano",
3939
Args: cobra.ExactArgs(1),
4040
Run: runDetailsCommand,
4141
}

Diff for: cli/board/list.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func initListCommand() *cobra.Command {
3737
Use: "list",
3838
Short: "List connected boards.",
3939
Long: "Detects and displays a list of connected boards to the current computer.",
40-
Example: " " + cli.AppName + " board list --timeout 10s",
40+
Example: " " + cli.VersionInfo.Application + " board list --timeout 10s",
4141
Args: cobra.NoArgs,
4242
Run: runListCommand,
4343
}

Diff for: cli/board/listall.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ func initListAllCommand() *cobra.Command {
3939
"List all boards that have the support platform installed. You can search\n" +
4040
"for a specific board if you specify the board name",
4141
Example: "" +
42-
" " + cli.AppName + " board listall\n" +
43-
" " + cli.AppName + " board listall zero",
42+
" " + cli.VersionInfo.Application + " board listall\n" +
43+
" " + cli.VersionInfo.Application + " board listall zero",
4444
Args: cobra.ArbitraryArgs,
4545
Run: runListAllCommand,
4646
}

Diff for: cli/cli.go

+22-8
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,20 @@ package cli
2020
import (
2121
"context"
2222
"errors"
23+
"fmt"
24+
"net/http"
2325
"os"
2426
"path/filepath"
27+
"runtime"
2528

2629
"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
2730
"github.com/arduino/arduino-cli/arduino/libraries/librariesmanager"
2831
"github.com/arduino/arduino-cli/commands"
2932
"github.com/arduino/arduino-cli/common/formatter"
3033
"github.com/arduino/arduino-cli/configs"
3134
"github.com/arduino/arduino-cli/rpc"
32-
paths "github.com/arduino/go-paths-helper"
35+
"github.com/arduino/arduino-cli/version"
36+
"github.com/arduino/go-paths-helper"
3337
"github.com/sirupsen/logrus"
3438
)
3539

@@ -48,8 +52,13 @@ const (
4852
ErrBadArgument
4953
)
5054

51-
// Version is the current CLI version
52-
var Version = "0.3.6-alpha.preview"
55+
// appName is the command line name of the Arduino CLI executable on the user system (users may change it)
56+
var appName = filepath.Base(os.Args[0])
57+
58+
// VersionInfo contains all info injected during build
59+
var VersionInfo = version.NewInfo(appName)
60+
61+
var HTTPClientHeader = getHTTPClientHeader()
5362

5463
// ErrLogrus represents the logrus instance, which has the role to
5564
// log all non info messages.
@@ -61,9 +70,6 @@ var GlobalFlags struct {
6170
OutputJSON bool // true output in JSON, false output as Text
6271
}
6372

64-
// AppName is the command line name of the Arduino CLI executable
65-
var AppName = filepath.Base(os.Args[0])
66-
6773
var Config *configs.Configuration
6874

6975
func packageManagerInitReq() *rpc.InitReq {
@@ -83,10 +89,18 @@ func packageManagerInitReq() *rpc.InitReq {
8389
return &rpc.InitReq{Configuration: conf}
8490
}
8591

92+
func getHTTPClientHeader() http.Header {
93+
userAgentValue := fmt.Sprintf("%s/%s (%s; %s; %s) Commit:%s/Build:%s", VersionInfo.Application,
94+
VersionInfo.VersionString, runtime.GOARCH, runtime.GOOS, runtime.Version(), VersionInfo.Commit, VersionInfo.BuildDate)
95+
downloaderHeaders := http.Header{"User-Agent": []string{userAgentValue}}
96+
return downloaderHeaders
97+
}
98+
8699
func InitInstance() *rpc.InitResp {
87100
logrus.Info("Initializing package manager")
88101
req := packageManagerInitReq()
89-
resp, err := commands.Init(context.Background(), req, OutputProgressBar(), OutputTaskProgress())
102+
103+
resp, err := commands.Init(context.Background(), req, OutputProgressBar(), OutputTaskProgress(), HTTPClientHeader)
90104
if err != nil {
91105
formatter.PrintError(err, "Error initializing package manager")
92106
os.Exit(ErrGeneric)
@@ -116,7 +130,7 @@ func CreateInstance() *rpc.Instance {
116130
for _, err := range resp.GetPlatformsIndexErrors() {
117131
formatter.PrintError(errors.New(err), "Error loading index")
118132
}
119-
formatter.PrintErrorMessage("Launch '" + AppName + " core update-index' to fix or download indexes.")
133+
formatter.PrintErrorMessage("Launch '" + VersionInfo.Application + " core update-index' to fix or download indexes.")
120134
os.Exit(ErrGeneric)
121135
}
122136
return resp.GetInstance()

Diff for: cli/compile/compile.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func InitCommand() *cobra.Command {
3535
Use: "compile",
3636
Short: "Compiles Arduino sketches.",
3737
Long: "Compiles Arduino sketches.",
38-
Example: " " + cli.AppName + " compile -b arduino:avr:uno /home/user/Arduino/MySketch",
38+
Example: " " + cli.VersionInfo.Application + " compile -b arduino:avr:uno /home/user/Arduino/MySketch",
3939
Args: cobra.MaximumNArgs(1),
4040
Run: run,
4141
}

0 commit comments

Comments
 (0)