Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ require (
github.com/arduino/go-paths-helper v1.14.0
github.com/arduino/go-windows-runas v1.0.1
github.com/codeclysm/extract/v4 v4.0.0
github.com/jedib0t/go-pretty/v6 v6.6.8
github.com/leonelquinteros/gotext v1.7.2
github.com/schollz/progressbar/v3 v3.18.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.9.0
github.com/stretchr/testify v1.10.0
go.bug.st/cleanup v1.0.0
go.bug.st/f v0.4.0
)

require (
Expand All @@ -20,12 +22,14 @@ require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/juju/errors v1.0.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/spf13/pflag v1.0.7 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/term v0.35.0 // indirect
golang.org/x/text v0.22.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
11 changes: 9 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jedib0t/go-pretty/v6 v6.6.8 h1:JnnzQeRz2bACBobIaa/r+nqjvws4yEhcmaZ4n1QzsEc=
github.com/jedib0t/go-pretty/v6 v6.6.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
github.com/juju/errors v1.0.0 h1:yiq7kjCLll1BiaRuNY53MGI0+EQ3rF6GB+wvboZDefM=
github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5Qe8=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
Expand All @@ -30,6 +32,7 @@ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2Em
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
Expand All @@ -45,17 +48,21 @@ github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
go.bug.st/cleanup v1.0.0 h1:XVj1HZxkBXeq3gMT7ijWUpHyIC1j8XAoNSyQ06CskgA=
go.bug.st/cleanup v1.0.0/go.mod h1:EqVmTg2IBk4znLbPD28xne3abjsJftMdqqJEjhn70bk=
go.bug.st/f v0.4.0 h1:Vstqb950nMA+PhAlRxUw8QL1ntHy/gXHNyyzjkQLJ10=
go.bug.st/f v0.4.0/go.mod h1:bMo23205ll7UW63KwO1ut5RdlJ9JK8RyEEr88CmOF5Y=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
Expand Down
75 changes: 75 additions & 0 deletions list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// This file is part of arduino-flasher-cli.
//
// Copyright 2025 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-flasher-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.

package main

import (
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"

"github.com/arduino/arduino-flasher-cli/feedback"
"github.com/arduino/arduino-flasher-cli/i18n"
"github.com/arduino/arduino-flasher-cli/tablestyle"
"github.com/arduino/arduino-flasher-cli/updater"
)

func newListCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List the available Linux images",
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
runListCommand()
},
}
return cmd
}

func runListCommand() {
client := updater.NewClient()

manifest, err := client.GetInfoManifest()
if err != nil {
feedback.Fatal(i18n.Tr("error retrieving the manifest: %v", err), feedback.ErrBadArgument)
}

feedback.PrintResult(listResult{Latest: manifest.Latest, Releases: manifest.Releases})
}

type listResult struct {
Latest updater.Release `json:"latest"`
Releases []updater.Release `json:"releases"`
}

// Data implements Result interface
func (lr listResult) Data() interface{} {
return lr
}

// String implements Result interface
func (lr listResult) String() string {
t := table.NewWriter()
t.SetStyle(tablestyle.CustomCleanStyle)
t.AppendHeader(table.Row{"VERSION", "LATEST"})

for i := len(lr.Releases) - 1; i >= 0; i-- {
row := table.Row{lr.Releases[i].Version}
if lr.Releases[i].Version == lr.Latest.Version {
row = append(row, "✓")
}
t.AppendRow(row)
}
return t.Render()
}
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func main() {
rootCmd.AddCommand(
newFlashCmd(),
newInstallDriversCmd(),
newListCmd(),
&cobra.Command{
Use: "version",
Short: "Print the version number of Arduino Flasher CLI",
Expand Down
41 changes: 41 additions & 0 deletions tablestyle/tablestyle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// This file is part of arduino-flasher-cli.
//
// Copyright 2025 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-flasher-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.

package tablestyle

import (
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
)

var (
CustomCleanStyle = table.Style{
Name: "CustomClean",
Box: table.BoxStyle{PaddingRight: " "},
Format: table.FormatOptions{
Footer: text.FormatUpper,
Header: text.FormatUpper,
Row: text.FormatDefault,
RowAlign: text.AlignCenter,
},
Options: table.Options{
DrawBorder: false,
SeparateColumns: true,
SeparateFooter: false,
SeparateHeader: false,
SeparateRows: false,
},
}
)
10 changes: 1 addition & 9 deletions updater/flasher.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package updater
import (
"context"
"fmt"
"net/url"
"runtime"
"strings"

Expand All @@ -31,14 +30,7 @@ import (

func Flash(ctx context.Context, imagePath *paths.Path, version string, forceYes bool) error {
if !imagePath.Exist() {
updateURL := "https://downloads.arduino.cc"

parsedURL, err := url.Parse(updateURL)
if err != nil {
return fmt.Errorf("invalid UPDATE_URL: %v", err)
}

client := NewClient(parsedURL, "debian-im/Stable")
client := NewClient()

tempImagePath, err := DownloadAndExtract(client, version, func(target string) (bool, error) {
feedback.Printf("Found Debian image version: %s", target)
Expand Down
14 changes: 8 additions & 6 deletions updater/http_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@ import (
"io"
"net/http"
"net/url"

"go.bug.st/f"
)

var baseURL = f.Must(url.Parse("https://downloads.arduino.cc"))

const pathRelease = "debian-im/Stable"

// Client holds the base URL, command name, allows custom HTTP client, and optional headers.
type Client struct {
BaseURL *url.URL
CmdName string
HTTPClient HTTPDoer
Headers map[string]string // Optional headers to add to each request
}
Expand Down Expand Up @@ -56,10 +60,8 @@ func WithHTTPClient(client HTTPDoer) Option {
}

// NewClient creates a new Client with optional configuration.
func NewClient(baseURL *url.URL, cmdName string, opts ...Option) *Client {
func NewClient(opts ...Option) *Client {
c := &Client{
BaseURL: baseURL,
CmdName: cmdName,
HTTPClient: http.DefaultClient,
Headers: nil,
}
Expand All @@ -78,7 +80,7 @@ func (c *Client) addHeaders(req *http.Request) {

// GetInfoManifest fetches and decodes the Debian images info.json.
func (c *Client) GetInfoManifest() (Manifest, error) {
manifestURL := c.BaseURL.JoinPath(c.CmdName, "info.json").String()
manifestURL := baseURL.JoinPath(pathRelease, "info.json").String()
req, err := http.NewRequest("GET", manifestURL, nil)
if err != nil {
return Manifest{}, fmt.Errorf("failed to create request: %w", err)
Expand Down