Skip to content

Commit df2d30e

Browse files
authored
Implemented dry-run for upload and burn-bootloader (#1352)
* Implemented dry-run for upload and burn-bootloader * Reduced Reset timeout in dry-run mode
1 parent e9c87a8 commit df2d30e

File tree

13 files changed

+249
-140
lines changed

13 files changed

+249
-140
lines changed

Diff for: arduino/serialutils/serialutils.go

+34-6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package serialutils
1717

1818
import (
1919
"fmt"
20+
"strings"
2021
"time"
2122

2223
"github.com/pkg/errors"
@@ -86,9 +87,28 @@ type ResetProgressCallbacks struct {
8687
// If wait is true, this function will wait for a new port to appear and returns that
8788
// one, otherwise the empty string is returned if the new port can not be detected or
8889
// if the wait parameter is false.
90+
// If dryRun is set to true this function will only emulate the port reset without actually
91+
// performing it, this is useful to mockup for unit-testing and CI.
92+
// In dryRun mode if the `portToTouch` ends with `"999"` and wait is true, Reset will
93+
// return a new "bootloader" port as `portToTouch+"0"`.
8994
// The error is set if the port listing fails.
90-
func Reset(portToTouch string, wait bool, cb *ResetProgressCallbacks) (string, error) {
91-
last, err := getPortMap()
95+
func Reset(portToTouch string, wait bool, cb *ResetProgressCallbacks, dryRun bool) (string, error) {
96+
getPorts := getPortMap // non dry-run default
97+
if dryRun {
98+
emulatedPort := portToTouch
99+
getPorts = func() (map[string]bool, error) {
100+
res := map[string]bool{}
101+
if emulatedPort != "" {
102+
res[emulatedPort] = true
103+
}
104+
if strings.HasSuffix(emulatedPort, "999") {
105+
emulatedPort += "0"
106+
}
107+
return res, nil
108+
}
109+
}
110+
111+
last, err := getPorts()
92112
if cb != nil && cb.Debug != nil {
93113
cb.Debug(fmt.Sprintf("LAST: %v", last))
94114
}
@@ -103,8 +123,12 @@ func Reset(portToTouch string, wait bool, cb *ResetProgressCallbacks) (string, e
103123
if cb != nil && cb.TouchingPort != nil {
104124
cb.TouchingPort(portToTouch)
105125
}
106-
if err := TouchSerialPortAt1200bps(portToTouch); err != nil {
107-
fmt.Println("TOUCH: error during reset:", err)
126+
if dryRun {
127+
// do nothing!
128+
} else {
129+
if err := TouchSerialPortAt1200bps(portToTouch); err != nil {
130+
fmt.Println("TOUCH: error during reset:", err)
131+
}
108132
}
109133
}
110134

@@ -116,8 +140,12 @@ func Reset(portToTouch string, wait bool, cb *ResetProgressCallbacks) (string, e
116140
}
117141

118142
deadline := time.Now().Add(10 * time.Second)
143+
if dryRun {
144+
// use a much lower timeout in dryRun
145+
deadline = time.Now().Add(100 * time.Millisecond)
146+
}
119147
for time.Now().Before(deadline) {
120-
now, err := getPortMap()
148+
now, err := getPorts()
121149
if err != nil {
122150
return "", err
123151
}
@@ -146,7 +174,7 @@ func Reset(portToTouch string, wait bool, cb *ResetProgressCallbacks) (string, e
146174
// the USB serial port appearing and disappearing rapidly before
147175
// settling.
148176
// This check ensure that the port is stable after one second.
149-
check, err := getPortMap()
177+
check, err := getPorts()
150178
if err != nil {
151179
return "", err
152180
}

Diff for: cli/burnbootloader/burnbootloader.go

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ var (
3333
verbose bool
3434
verify bool
3535
programmer string
36+
dryRun bool
3637
)
3738

3839
// NewCommand created a new `burn-bootloader` command
@@ -51,6 +52,8 @@ func NewCommand() *cobra.Command {
5152
burnBootloaderCommand.Flags().BoolVarP(&verify, "verify", "t", false, "Verify uploaded binary after the upload.")
5253
burnBootloaderCommand.Flags().BoolVarP(&verbose, "verbose", "v", false, "Turns on verbose mode.")
5354
burnBootloaderCommand.Flags().StringVarP(&programmer, "programmer", "P", "", "Use the specified programmer to upload.")
55+
burnBootloaderCommand.Flags().BoolVar(&dryRun, "dry-run", false, "Do not perform the actual upload, just log out actions")
56+
burnBootloaderCommand.Flags().MarkHidden("dry-run")
5457

5558
return burnBootloaderCommand
5659
}
@@ -65,6 +68,7 @@ func run(command *cobra.Command, args []string) {
6568
Verbose: verbose,
6669
Verify: verify,
6770
Programmer: programmer,
71+
DryRun: dryRun,
6872
}, os.Stdout, os.Stderr); err != nil {
6973
feedback.Errorf("Error during Upload: %v", err)
7074
os.Exit(errorcodes.ErrGeneric)

Diff for: cli/upload/upload.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ var (
3838
importDir string
3939
importFile string
4040
programmer string
41+
dryRun bool
4142
)
4243

4344
// NewCommand created a new `upload` command
@@ -59,7 +60,8 @@ func NewCommand() *cobra.Command {
5960
uploadCommand.Flags().BoolVarP(&verify, "verify", "t", false, "Verify uploaded binary after the upload.")
6061
uploadCommand.Flags().BoolVarP(&verbose, "verbose", "v", false, "Optional, turns on verbose mode.")
6162
uploadCommand.Flags().StringVarP(&programmer, "programmer", "P", "", "Optional, use the specified programmer to upload.")
62-
63+
uploadCommand.Flags().BoolVar(&dryRun, "dry-run", false, "Do not perform the actual upload, just log out actions")
64+
uploadCommand.Flags().MarkHidden("dry-run")
6365
return uploadCommand
6466
}
6567

@@ -97,6 +99,7 @@ func run(command *cobra.Command, args []string) {
9799
ImportFile: importFile,
98100
ImportDir: importDir,
99101
Programmer: programmer,
102+
DryRun: dryRun,
100103
}, os.Stdout, os.Stderr); err != nil {
101104
feedback.Errorf("Error during Upload: %v", err)
102105
os.Exit(errorcodes.ErrGeneric)

Diff for: commands/upload/burnbootloader.go

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ func BurnBootloader(ctx context.Context, req *rpc.BurnBootloaderRequest, outStre
4747
true, // burnBootloader
4848
outStream,
4949
errStream,
50+
req.GetDryRun(),
5051
)
5152
if err != nil {
5253
return nil, err

Diff for: commands/upload/upload.go

+37-24
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, er
6565
false, // burnBootloader
6666
outStream,
6767
errStream,
68+
req.GetDryRun(),
6869
)
6970
if err != nil {
7071
return nil, err
@@ -98,7 +99,8 @@ func runProgramAction(pm *packagemanager.PackageManager,
9899
importFile, importDir, fqbnIn, port string,
99100
programmerID string,
100101
verbose, verify, burnBootloader bool,
101-
outStream, errStream io.Writer) error {
102+
outStream, errStream io.Writer,
103+
dryRun bool) error {
102104

103105
if burnBootloader && programmerID == "" {
104106
return fmt.Errorf("no programmer specified for burning bootloader")
@@ -308,35 +310,42 @@ func runProgramAction(pm *packagemanager.PackageManager,
308310
outStream.Write([]byte(fmt.Sprintln("Skipping 1200-bps touch reset: no serial port selected!")))
309311
}
310312

311-
var cb *serialutils.ResetProgressCallbacks
312-
if verbose {
313-
cb = &serialutils.ResetProgressCallbacks{
314-
TouchingPort: func(port string) {
315-
logrus.WithField("phase", "board reset").Infof("Performing 1200-bps touch reset on serial port %s", port)
313+
cb := &serialutils.ResetProgressCallbacks{
314+
TouchingPort: func(port string) {
315+
logrus.WithField("phase", "board reset").Infof("Performing 1200-bps touch reset on serial port %s", port)
316+
if verbose {
316317
outStream.Write([]byte(fmt.Sprintf("Performing 1200-bps touch reset on serial port %s", port)))
317318
outStream.Write([]byte(fmt.Sprintln()))
318-
},
319-
WaitingForNewSerial: func() {
320-
logrus.WithField("phase", "board reset").Info("Waiting for upload port...")
319+
}
320+
},
321+
WaitingForNewSerial: func() {
322+
logrus.WithField("phase", "board reset").Info("Waiting for upload port...")
323+
if verbose {
321324
outStream.Write([]byte(fmt.Sprintln("Waiting for upload port...")))
322-
},
323-
BootloaderPortFound: func(port string) {
325+
}
326+
},
327+
BootloaderPortFound: func(port string) {
328+
if port != "" {
329+
logrus.WithField("phase", "board reset").Infof("Upload port found on %s", port)
330+
} else {
331+
logrus.WithField("phase", "board reset").Infof("No upload port found, using %s as fallback", actualPort)
332+
}
333+
if verbose {
324334
if port != "" {
325-
logrus.WithField("phase", "board reset").Infof("Upload port found on %s", port)
326335
outStream.Write([]byte(fmt.Sprintf("Upload port found on %s", port)))
327336
outStream.Write([]byte(fmt.Sprintln()))
328337
} else {
329-
logrus.WithField("phase", "board reset").Infof("No upload port found, using %s as fallback", actualPort)
330338
outStream.Write([]byte(fmt.Sprintf("No upload port found, using %s as fallback", actualPort)))
331339
outStream.Write([]byte(fmt.Sprintln()))
332340
}
333-
},
334-
Debug: func(msg string) {
335-
logrus.WithField("phase", "board reset").Debug(msg)
336-
},
337-
}
341+
}
342+
},
343+
Debug: func(msg string) {
344+
logrus.WithField("phase", "board reset").Debug(msg)
345+
},
338346
}
339-
if newPort, err := serialutils.Reset(portToTouch, wait, cb); err != nil {
347+
348+
if newPort, err := serialutils.Reset(portToTouch, wait, cb, dryRun); err != nil {
340349
outStream.Write([]byte(fmt.Sprintf("Cannot perform port reset: %s", err)))
341350
outStream.Write([]byte(fmt.Sprintln()))
342351
} else {
@@ -358,18 +367,18 @@ func runProgramAction(pm *packagemanager.PackageManager,
358367

359368
// Run recipes for upload
360369
if burnBootloader {
361-
if err := runTool("erase.pattern", uploadProperties, outStream, errStream, verbose); err != nil {
370+
if err := runTool("erase.pattern", uploadProperties, outStream, errStream, verbose, dryRun); err != nil {
362371
return fmt.Errorf("chip erase error: %s", err)
363372
}
364-
if err := runTool("bootloader.pattern", uploadProperties, outStream, errStream, verbose); err != nil {
373+
if err := runTool("bootloader.pattern", uploadProperties, outStream, errStream, verbose, dryRun); err != nil {
365374
return fmt.Errorf("burn bootloader error: %s", err)
366375
}
367376
} else if programmer != nil {
368-
if err := runTool("program.pattern", uploadProperties, outStream, errStream, verbose); err != nil {
377+
if err := runTool("program.pattern", uploadProperties, outStream, errStream, verbose, dryRun); err != nil {
369378
return fmt.Errorf("programming error: %s", err)
370379
}
371380
} else {
372-
if err := runTool("upload.pattern", uploadProperties, outStream, errStream, verbose); err != nil {
381+
if err := runTool("upload.pattern", uploadProperties, outStream, errStream, verbose, dryRun); err != nil {
373382
return fmt.Errorf("uploading error: %s", err)
374383
}
375384
}
@@ -378,7 +387,7 @@ func runProgramAction(pm *packagemanager.PackageManager,
378387
return nil
379388
}
380389

381-
func runTool(recipeID string, props *properties.Map, outStream, errStream io.Writer, verbose bool) error {
390+
func runTool(recipeID string, props *properties.Map, outStream, errStream io.Writer, verbose bool, dryRun bool) error {
382391
recipe, ok := props.GetOk(recipeID)
383392
if !ok {
384393
return fmt.Errorf("recipe not found '%s'", recipeID)
@@ -396,9 +405,13 @@ func runTool(recipeID string, props *properties.Map, outStream, errStream io.Wri
396405
}
397406

398407
// Run Tool
408+
logrus.WithField("phase", "upload").Tracef("Executing upload tool: %s", cmdLine)
399409
if verbose {
400410
outStream.Write([]byte(fmt.Sprintln(cmdLine)))
401411
}
412+
if dryRun {
413+
return nil
414+
}
402415
cmd, err := executils.NewProcess(cmdArgs...)
403416
if err != nil {
404417
return fmt.Errorf("cannot execute upload tool: %s", err)

Diff for: commands/upload/upload_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ func TestUploadPropertiesComposition(t *testing.T) {
191191
test.burnBootloader, // burnBootloader
192192
outStream,
193193
errStream,
194+
false,
194195
)
195196
verboseVerifyOutput := "verbose verify"
196197
if !verboseVerify {

0 commit comments

Comments
 (0)