From 6904510ba4e4e3db67fa3f7d0d9b5bc682cace09 Mon Sep 17 00:00:00 2001 From: Florian Loitsch Date: Mon, 28 Oct 2024 09:50:36 +0100 Subject: [PATCH] Refactor flash and update to share more code. --- cmd/jag/commands/firmware.go | 375 +++-------------------------- cmd/jag/commands/firmware_flash.go | 368 ++++++++++++++++++++++++++++ cmd/jag/commands/flash.go | 131 ++-------- 3 files changed, 422 insertions(+), 452 deletions(-) create mode 100644 cmd/jag/commands/firmware_flash.go diff --git a/cmd/jag/commands/firmware.go b/cmd/jag/commands/firmware.go index ddbf276..221e9b8 100644 --- a/cmd/jag/commands/firmware.go +++ b/cmd/jag/commands/firmware.go @@ -5,14 +5,9 @@ package commands import ( - "context" - "encoding/json" "fmt" - "io" "os" - "path/filepath" - "github.com/google/uuid" "github.com/spf13/cobra" "github.com/toitlang/jaguar/cmd/jag/directory" ) @@ -54,10 +49,9 @@ func FirmwareCmd() *cobra.Command { func FirmwareUpdateCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "update [envelope]", - Short: "Update the firmware on a Jaguar device", - Long: "Update the firmware on a Jaguar device via WiFi. The device name and\n" + - "id are preserved across the operation.", + Use: "update [envelope]", + Short: "Update the firmware on a Jaguar device", + Long: "Update the firmware on a Jaguar device via WiFi", Args: cobra.MaximumNArgs(1), SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -72,361 +66,48 @@ func FirmwareUpdateCmd() *cobra.Command { return err } - // We need to generate a new ID for the device, so entries in - // the device flash stored by an older version are invalidated. - newID := uuid.New().String() - device, err := GetDevice(ctx, sdk, true, deviceSelect) if err != nil { return err } - chip, err := cmd.Flags().GetString("chip") - if err != nil { - return err - } - - if chip == "auto" || chip == "" { - chip = device.Chip() - } + // We get a new ID for the device, so entries in the device flash stored + // by an older version are invalidated. + return withFirmware(cmd, args, device, func(newID string, envelopeFile *os.File, config map[string]interface{}) error { - wifiSSID, wifiPassword, err := getWifiCredentials(cmd) - if err != nil { - return err - } - - deviceOptions := DeviceOptions{ - Id: newID, - Name: device.Name(), - Chip: chip, - WifiSsid: wifiSSID, - WifiPassword: wifiPassword, - } - - var envelopePath string - if len(args) == 1 { - // Make a temporary directory for the downloaded envelope. - tmpDir, err := os.MkdirTemp("", "*-envelope") + firmwareBin, err := ExtractFirmwareBin(ctx, sdk, envelopeFile.Name(), config) if err != nil { return err } - defer os.RemoveAll(tmpDir) - envelopePath, err = DownloadEnvelope(ctx, args[0], sdk.Version, tmpDir) - } else { - envelopePath, err = GetCachedFirmwareEnvelopePath(ctx, sdk.Version, chip) + defer os.Remove(firmwareBin.Name()) + + bin, err := os.ReadFile(firmwareBin.Name()) if err != nil { return err } - } - - excludeJaguar, err := cmd.Flags().GetBool("exclude-jaguar") - if err != nil { - return err - } - - envelopeOptions := EnvelopeOptions{ - Path: envelopePath, - ExcludeJaguar: excludeJaguar, - } - - uartEndpointOptions, err := getUartEndpointOptions(cmd) - if err != nil { - return err - } - - envelopeFile, err := BuildFirmwareEnvelope(ctx, envelopeOptions, deviceOptions, uartEndpointOptions) - if err != nil { - return err - } - defer os.Remove(envelopeFile.Name()) - - config := deviceOptions.GetConfig() - firmwareBin, err := ExtractFirmwareBin(ctx, sdk, envelopeFile.Name(), config) - if err != nil { - return err - } - defer os.Remove(firmwareBin.Name()) - - bin, err := os.ReadFile(firmwareBin.Name()) - if err != nil { - return err - } - fmt.Printf("Updating firmware on '%s' to Toit SDK %s\n\n", device.Name(), sdk.Version) - if err := device.UpdateFirmware(ctx, sdk, bin); err != nil { - return err - } + fmt.Printf("Updating firmware on '%s' to Toit SDK %s\n\n", device.Name(), sdk.Version) + if err := device.UpdateFirmware(ctx, sdk, bin); err != nil { + return err + } - // Update the device ID and the SDK version and store them back, so users don't - // have to scan and ping before they can use the device after the firmware update. - // If the update failed or if the device got a new IP address after rebooting, we - // will have to ping again. - device.SetID(newID) - device.SetSDKVersion(sdk.Version) - deviceCfg, err := directory.GetDeviceConfig() - if err != nil { - return err - } - deviceCfg.Set("device", device.ToJson()) - return deviceCfg.WriteConfig() + // Update the device ID and the SDK version and store them back, so users don't + // have to scan and ping before they can use the device after the firmware update. + // If the update failed or if the device got a new IP address after rebooting, we + // will have to ping again. + device.SetID(newID) + device.SetSDKVersion(sdk.Version) + deviceCfg, err := directory.GetDeviceConfig() + if err != nil { + return err + } + deviceCfg.Set("device", device.ToJson()) + return deviceCfg.WriteConfig() + }) }, } - cmd.Flags().StringP("chip", "c", "", "chip of the target device") - cmd.Flags().String("wifi-ssid", "", "default WiFi network name") - cmd.Flags().String("wifi-password", "", "default WiFi password") cmd.Flags().StringP("device", "d", "", "use device with a given name, id, or address") - cmd.Flags().Bool("exclude-jaguar", false, "don't install the Jaguar service") - cmd.Flags().Int("uart-endpoint-rx", -1, "add a UART endpoint to the device listening on the given pin") - cmd.Flags().MarkHidden("uart-endpoint-rx") - cmd.Flags().Uint("uart-endpoint-baud", 0, "set the baud rate for the UART endpoint") - cmd.Flags().MarkHidden("uart-endpoint-baud") + addFirmwareFlashFlags(cmd, "", "new name of the device, if given") return cmd } - -type DeviceOptions struct { - Id string - Name string - Chip string - WifiSsid string - WifiPassword string -} - -type EnvelopeOptions struct { - Path string - ExcludeJaguar bool -} - -func (d DeviceOptions) GetConfig() map[string]interface{} { - return map[string]interface{}{ - "wifi": map[string]string{ - "wifi.ssid": d.WifiSsid, - "wifi.password": d.WifiPassword, - }, - } -} - -func BuildFirmwareEnvelope(ctx context.Context, envelope EnvelopeOptions, device DeviceOptions, uartEndpointOptions map[string]interface{}) (*os.File, error) { - sdk, err := GetSDK(ctx) - if err != nil { - return nil, err - } - - envelopeFile, err := os.CreateTemp("", "*.envelope") - if err != nil { - return nil, err - } - defer envelopeFile.Close() - - if envelope.ExcludeJaguar { - source, err := os.Open(envelope.Path) - if err != nil { - return nil, err - } - _, err = io.Copy(envelopeFile, source) - source.Close() - if err != nil { - return nil, err - } - } else { - jaguarSnapshot, err := directory.GetJaguarSnapshotPath() - if err != nil { - return nil, err - } - - configAssetMap := map[string]interface{}{ - "id": device.Id, - "name": device.Name, - "chip": device.Chip, - } - if uartEndpointOptions != nil { - configAssetMap["endpointUart"] = uartEndpointOptions - } - configAssetJson, err := json.Marshal(configAssetMap) - if err != nil { - return nil, err - } - - configAssetJsonFile, err := os.CreateTemp("", "*.json.assets") - if err != nil { - return nil, err - } - defer configAssetJsonFile.Close() - - if err := os.WriteFile(configAssetJsonFile.Name(), configAssetJson, 0666); err != nil { - return nil, err - } - - assetsFile, err := os.CreateTemp("", "*.assets") - if err != nil { - return nil, err - } - defer assetsFile.Close() - - if err := runAssetsTool(ctx, sdk, assetsFile.Name(), "create"); err != nil { - return nil, err - } - - if err := runAssetsTool(ctx, sdk, assetsFile.Name(), "add", "--format=tison", "config", configAssetJsonFile.Name()); err != nil { - return nil, err - } - - if err := runFirmwareTool(ctx, sdk, envelope.Path, - "container", "install", "-o", envelopeFile.Name(), - "--assets", assetsFile.Name(), - "--trigger=boot", "--critical", - "jaguar", jaguarSnapshot); err != nil { - return nil, err - } - } - - if err := setFirmwareProperty(ctx, sdk, envelopeFile, "uuid", device.Id); err != nil { - return nil, err - } - - if err := copySnapshotsIntoCache(ctx, sdk, envelopeFile); err != nil { - return nil, err - } - - return envelopeFile, nil -} - -func ExtractFirmwareBin(ctx context.Context, sdk *SDK, envelopePath string, config map[string]interface{}) (*os.File, error) { - binaryFile, err := os.CreateTemp("", "firmware.bin.*") - if err != nil { - return nil, err - } - - arguments := []string{ - "extract", - "--format=binary", - "-o", binaryFile.Name(), - } - - if err := runFirmwareToolWithConfig(ctx, sdk, envelopePath, config, arguments...); err != nil { - binaryFile.Close() - return nil, err - } - return binaryFile, nil -} - -func ExtractFirmware(ctx context.Context, sdk *SDK, envelopePath string, format string) (*os.File, error) { - outputFile, err := os.CreateTemp("", "firmware-"+format+".*") - if err != nil { - return nil, err - } - if err := runFirmwareTool(ctx, sdk, envelopePath, "extract", "--format", format, "-o", outputFile.Name()); err != nil { - outputFile.Close() - return nil, err - } - return outputFile, nil -} - -func setFirmwareProperty(ctx context.Context, sdk *SDK, envelope *os.File, key string, value string) error { - return runFirmwareTool(ctx, sdk, envelope.Name(), "property", "set", key, value) -} - -func runFirmwareToolWithConfig(ctx context.Context, sdk *SDK, envelopePath string, config map[string]interface{}, args ...string) error { - configFile, err := os.CreateTemp("", "*.json.config") - if err != nil { - return err - } - defer os.Remove(configFile.Name()) - - configBytes, err := json.Marshal(config) - if err != nil { - return err - } - - if err := os.WriteFile(configFile.Name(), configBytes, 0666); err != nil { - return err - } - - args = append(args, "--config", configFile.Name()) - return runFirmwareTool(ctx, sdk, envelopePath, args...) -} - -func runFirmwareTool(ctx context.Context, sdk *SDK, envelopePath string, args ...string) error { - args = append([]string{"-e", envelopePath}, args...) - cmd := sdk.FirmwareTool(ctx, args...) - cmd.Stderr = os.Stderr - cmd.Stdout = os.Stdout - return cmd.Run() -} - -func copySnapshotsIntoCache(ctx context.Context, sdk *SDK, envelope *os.File) error { - listFile, err := os.CreateTemp("", "firmware-list.*") - if err != nil { - return err - } - defer os.Remove(listFile.Name()) - - if err := runFirmwareTool(ctx, sdk, envelope.Name(), "container", "list", "--output-format", "json", "-o", listFile.Name()); err != nil { - return err - } - - listBytes, err := os.ReadFile(listFile.Name()) - if err != nil { - return err - } - - var entries map[string]map[string]interface{} - if err := json.Unmarshal(listBytes, &entries); err != nil { - return err - } - - for name, entry := range entries { - kind := entry["kind"] - if kind != "snapshot" { - continue - } - - snapshotFile, err := os.CreateTemp("", "firmware-snapshot.*") - if err != nil { - return err - } - defer os.Remove(snapshotFile.Name()) - - snapshotExtractArguments := []string{ - "container", "extract", - "-o", snapshotFile.Name(), - "--part=image", - name, - } - if err := runFirmwareTool(ctx, sdk, envelope.Name(), snapshotExtractArguments...); err != nil { - return err - } - - if err := copySnapshotIntoCache(snapshotFile.Name()); err != nil { - return err - } - } - return nil -} - -func copySnapshotIntoCache(path string) error { - uuid, err := GetUuid(path) - if err != nil { - return err - } - - stateDirectory, err := directory.GetSnapshotsStatePath() - if err != nil { - return err - } - - source, err := os.Open(path) - if err != nil { - return err - } - defer source.Close() - - destination, err := os.Create(filepath.Join(stateDirectory, uuid.String()+".snapshot")) - if err != nil { - return err - } - defer destination.Close() - - _, err = io.Copy(destination, source) - return err -} diff --git a/cmd/jag/commands/firmware_flash.go b/cmd/jag/commands/firmware_flash.go new file mode 100644 index 0000000..c00708d --- /dev/null +++ b/cmd/jag/commands/firmware_flash.go @@ -0,0 +1,368 @@ +// Copyright (C) 2024 Toitware ApS. All rights reserved. +// Use of this source code is governed by an MIT-style license that can be +// found in the LICENSE file. + +package commands + +import ( + "context" + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + + "github.com/google/uuid" + "github.com/spf13/cobra" + "github.com/toitlang/jaguar/cmd/jag/directory" +) + +type callback func(id string, envelopeFile *os.File, config map[string]interface{}) error + +func addFirmwareFlashFlags(cmd *cobra.Command, defaultChip string, nameHelp string) { + cmd.Flags().String("name", "", nameHelp) + cmd.Flags().StringP("chip", "c", defaultChip, "chip of the target device") + cmd.Flags().String("wifi-ssid", "", "default WiFi network name") + cmd.Flags().String("wifi-password", "", "default WiFi password") + cmd.Flags().Bool("exclude-jaguar", false, "don't install the Jaguar service") + cmd.Flags().Int("uart-endpoint-rx", -1, "add a UART endpoint to the device listening on the given pin") + cmd.Flags().MarkHidden("uart-endpoint-rx") + cmd.Flags().Uint("uart-endpoint-baud", 0, "set the baud rate for the UART endpoint") + cmd.Flags().MarkHidden("uart-endpoint-baud") +} + +func withFirmware(cmd *cobra.Command, args []string, device Device, fun callback) error { + ctx := cmd.Context() + + sdk, err := GetSDK(ctx) + if err != nil { + return err + } + + chip, err := cmd.Flags().GetString("chip") + if err != nil { + return err + } + + if chip == "auto" || chip == "" { + if device != nil { + chip = device.Chip() + } else { + return fmt.Errorf("chip type must be specified") + } + } + + wifiSSID, wifiPassword, err := getWifiCredentials(cmd) + if err != nil { + return err + } + + id := uuid.New() + var name string + if cmd.Flags().Changed("name") { + name, err = cmd.Flags().GetString("name") + if err != nil { + return err + } + } else if device != nil { + name = device.Name() + } else { + name = GetRandomName(id[:]) + } + + deviceOptions := DeviceOptions{ + Id: id.String(), + Name: name, + Chip: chip, + WifiSsid: wifiSSID, + WifiPassword: wifiPassword, + } + + var envelopePath string + if len(args) == 1 { + // Make a temporary directory for the downloaded envelope. + tmpDir, err := os.MkdirTemp("", "*-envelope") + if err != nil { + return err + } + defer os.RemoveAll(tmpDir) + envelopePath, err = DownloadEnvelope(ctx, args[0], sdk.Version, tmpDir) + } else { + envelopePath, err = GetCachedFirmwareEnvelopePath(ctx, sdk.Version, chip) + if err != nil { + return err + } + } + + excludeJaguar, err := cmd.Flags().GetBool("exclude-jaguar") + if err != nil { + return err + } + + envelopeOptions := EnvelopeOptions{ + Path: envelopePath, + ExcludeJaguar: excludeJaguar, + } + + uartEndpointOptions, err := getUartEndpointOptions(cmd) + if err != nil { + return err + } + + envelopeFile, err := BuildFirmwareEnvelope(ctx, envelopeOptions, deviceOptions, uartEndpointOptions) + if err != nil { + return err + } + defer os.Remove(envelopeFile.Name()) + + config := deviceOptions.GetConfig() + + return fun(id.String(), envelopeFile, config) +} + +func BuildFirmwareEnvelope(ctx context.Context, envelope EnvelopeOptions, device DeviceOptions, uartEndpointOptions map[string]interface{}) (*os.File, error) { + sdk, err := GetSDK(ctx) + if err != nil { + return nil, err + } + + envelopeFile, err := os.CreateTemp("", "*.envelope") + if err != nil { + return nil, err + } + defer envelopeFile.Close() + + if envelope.ExcludeJaguar { + source, err := os.Open(envelope.Path) + if err != nil { + return nil, err + } + _, err = io.Copy(envelopeFile, source) + source.Close() + if err != nil { + return nil, err + } + } else { + jaguarSnapshot, err := directory.GetJaguarSnapshotPath() + if err != nil { + return nil, err + } + + configAssetMap := map[string]interface{}{ + "id": device.Id, + "name": device.Name, + "chip": device.Chip, + } + if uartEndpointOptions != nil { + configAssetMap["endpointUart"] = uartEndpointOptions + } + configAssetJson, err := json.Marshal(configAssetMap) + if err != nil { + return nil, err + } + + configAssetJsonFile, err := os.CreateTemp("", "*.json.assets") + if err != nil { + return nil, err + } + defer configAssetJsonFile.Close() + + if err := os.WriteFile(configAssetJsonFile.Name(), configAssetJson, 0666); err != nil { + return nil, err + } + + assetsFile, err := os.CreateTemp("", "*.assets") + if err != nil { + return nil, err + } + defer assetsFile.Close() + + if err := runAssetsTool(ctx, sdk, assetsFile.Name(), "create"); err != nil { + return nil, err + } + + if err := runAssetsTool(ctx, sdk, assetsFile.Name(), "add", "--format=tison", "config", configAssetJsonFile.Name()); err != nil { + return nil, err + } + + if err := runFirmwareTool(ctx, sdk, envelope.Path, + "container", "install", "-o", envelopeFile.Name(), + "--assets", assetsFile.Name(), + "--trigger=boot", "--critical", + "jaguar", jaguarSnapshot); err != nil { + return nil, err + } + } + + if err := setFirmwareProperty(ctx, sdk, envelopeFile, "uuid", device.Id); err != nil { + return nil, err + } + + if err := copySnapshotsIntoCache(ctx, sdk, envelopeFile); err != nil { + return nil, err + } + + return envelopeFile, nil +} + +type DeviceOptions struct { + Id string + Name string + Chip string + WifiSsid string + WifiPassword string +} + +type EnvelopeOptions struct { + Path string + ExcludeJaguar bool +} + +func (d DeviceOptions) GetConfig() map[string]interface{} { + return map[string]interface{}{ + "wifi": map[string]string{ + "wifi.ssid": d.WifiSsid, + "wifi.password": d.WifiPassword, + }, + } +} + +func ExtractFirmwareBin(ctx context.Context, sdk *SDK, envelopePath string, config map[string]interface{}) (*os.File, error) { + binaryFile, err := os.CreateTemp("", "firmware.bin.*") + if err != nil { + return nil, err + } + + arguments := []string{ + "extract", + "--format=binary", + "-o", binaryFile.Name(), + } + + if err := runFirmwareToolWithConfig(ctx, sdk, envelopePath, config, arguments...); err != nil { + binaryFile.Close() + return nil, err + } + return binaryFile, nil +} + +func ExtractFirmware(ctx context.Context, sdk *SDK, envelopePath string, format string) (*os.File, error) { + outputFile, err := os.CreateTemp("", "firmware-"+format+".*") + if err != nil { + return nil, err + } + if err := runFirmwareTool(ctx, sdk, envelopePath, "extract", "--format", format, "-o", outputFile.Name()); err != nil { + outputFile.Close() + return nil, err + } + return outputFile, nil +} + +func setFirmwareProperty(ctx context.Context, sdk *SDK, envelope *os.File, key string, value string) error { + return runFirmwareTool(ctx, sdk, envelope.Name(), "property", "set", key, value) +} + +func runFirmwareToolWithConfig(ctx context.Context, sdk *SDK, envelopePath string, config map[string]interface{}, args ...string) error { + configFile, err := os.CreateTemp("", "*.json.config") + if err != nil { + return err + } + defer os.Remove(configFile.Name()) + + configBytes, err := json.Marshal(config) + if err != nil { + return err + } + + if err := os.WriteFile(configFile.Name(), configBytes, 0666); err != nil { + return err + } + + args = append(args, "--config", configFile.Name()) + return runFirmwareTool(ctx, sdk, envelopePath, args...) +} + +func runFirmwareTool(ctx context.Context, sdk *SDK, envelopePath string, args ...string) error { + args = append([]string{"-e", envelopePath}, args...) + cmd := sdk.FirmwareTool(ctx, args...) + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + return cmd.Run() +} + +func copySnapshotsIntoCache(ctx context.Context, sdk *SDK, envelope *os.File) error { + listFile, err := os.CreateTemp("", "firmware-list.*") + if err != nil { + return err + } + defer os.Remove(listFile.Name()) + + if err := runFirmwareTool(ctx, sdk, envelope.Name(), "container", "list", "--output-format", "json", "-o", listFile.Name()); err != nil { + return err + } + + listBytes, err := os.ReadFile(listFile.Name()) + if err != nil { + return err + } + + var entries map[string]map[string]interface{} + if err := json.Unmarshal(listBytes, &entries); err != nil { + return err + } + + for name, entry := range entries { + kind := entry["kind"] + if kind != "snapshot" { + continue + } + + snapshotFile, err := os.CreateTemp("", "firmware-snapshot.*") + if err != nil { + return err + } + defer os.Remove(snapshotFile.Name()) + + snapshotExtractArguments := []string{ + "container", "extract", + "-o", snapshotFile.Name(), + "--part=image", + name, + } + if err := runFirmwareTool(ctx, sdk, envelope.Name(), snapshotExtractArguments...); err != nil { + return err + } + + if err := copySnapshotIntoCache(snapshotFile.Name()); err != nil { + return err + } + } + return nil +} + +func copySnapshotIntoCache(path string) error { + uuid, err := GetUuid(path) + if err != nil { + return err + } + + stateDirectory, err := directory.GetSnapshotsStatePath() + if err != nil { + return err + } + + source, err := os.Open(path) + if err != nil { + return err + } + defer source.Close() + + destination, err := os.Create(filepath.Join(stateDirectory, uuid.String()+".snapshot")) + if err != nil { + return err + } + defer destination.Close() + + _, err = io.Copy(destination, source) + return err +} diff --git a/cmd/jag/commands/flash.go b/cmd/jag/commands/flash.go index db91b29..3434521 100644 --- a/cmd/jag/commands/flash.go +++ b/cmd/jag/commands/flash.go @@ -9,7 +9,6 @@ import ( "os" "strconv" - "github.com/google/uuid" "github.com/spf13/cobra" ) @@ -24,10 +23,6 @@ func FlashCmd() *cobra.Command { SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() - sdk, err := GetSDK(ctx) - if err != nil { - return err - } port, err := cmd.Flags().GetString("port") if err != nil { @@ -48,120 +43,46 @@ func FlashCmd() *cobra.Command { return err } - chip, err := cmd.Flags().GetString("chip") - if err != nil { - return err - } + return withFirmware(cmd, args, nil, func(id string, envelopeFile *os.File, config map[string]interface{}) error { - if chip == "auto" { - return fmt.Errorf("auto-detecting chip type isn't supported yet") - } - - id := uuid.New() - var name string - if cmd.Flags().Changed("name") { - name, err = cmd.Flags().GetString("name") + sdk, err := GetSDK(ctx) if err != nil { return err } - } else { - name = GetRandomName(id[:]) - } - - wifiSSID, wifiPassword, err := getWifiCredentials(cmd) - if err != nil { - return err - } - - deviceOptions := DeviceOptions{ - Id: id.String(), - Name: name, - Chip: chip, - WifiSsid: wifiSSID, - WifiPassword: wifiPassword, - } - var envelopePath string - if len(args) == 1 { - // Make a temporary directory for the downloaded envelope. - tmpDir, err := os.MkdirTemp("", "*-envelope") - if err != nil { - return err + flashArguments := []string{ + "flash", + "--port", port, + "--baud", strconv.Itoa(int(baud)), } - defer os.RemoveAll(tmpDir) - envelopePath, err = DownloadEnvelope(ctx, args[0], sdk.Version, tmpDir) - if err != nil { - return err - } - } else { - envelopePath, err = GetCachedFirmwareEnvelopePath(ctx, sdk.Version, chip) - if err != nil { - return err - } - } - - excludeJaguar, err := cmd.Flags().GetBool("exclude-jaguar") - if err != nil { - return err - } - envelopeOptions := EnvelopeOptions{ - Path: envelopePath, - ExcludeJaguar: excludeJaguar, - } - - uartEndpointOptions, err := getUartEndpointOptions(cmd) - if err != nil { - return err - } - - envelopeFile, err := BuildFirmwareEnvelope(ctx, envelopeOptions, deviceOptions, uartEndpointOptions) - if err != nil { - return err - } - defer os.Remove(envelopeFile.Name()) - - flashArguments := []string{ - "flash", - "--port", port, - "--baud", strconv.Itoa(int(baud)), - } - - // Golang equivalent of #ifdef Windows. We skip this - // because the whole uucp group issue does not affect - // Windows, but on the other hand Windows has strange - // escaping rules for COM ports over 10 (COM10, COM11), - // which we don't want to deal with. - if os.PathSeparator != '\\' && !shouldSkipPortCheck { - // Use golang to check the port can be opened for writing first. - // This is to avoid the error message from esptool.py, which is - // confusing to users in the common case where the port is owned - // by the dialout or uucp group. - file, err := os.OpenFile(port, os.O_WRONLY, 0) - if err != nil { - return err + // Golang equivalent of #ifdef Windows. We skip this + // because the whole uucp group issue does not affect + // Windows, but on the other hand Windows has strange + // escaping rules for COM ports over 10 (COM10, COM11), + // which we don't want to deal with. + if os.PathSeparator != '\\' && !shouldSkipPortCheck { + // Use golang to check the port can be opened for writing first. + // This is to avoid the error message from esptool.py, which is + // confusing to users in the common case where the port is owned + // by the dialout or uucp group. + file, err := os.OpenFile(port, os.O_WRONLY, 0) + if err != nil { + return err + } + // Close the file again: + file.Close() } - // Close the file again: - file.Close() - } - fmt.Printf("Flashing device over serial on port '%s' ...\n", port) - config := deviceOptions.GetConfig() - return runFirmwareToolWithConfig(ctx, sdk, envelopeFile.Name(), config, flashArguments...) + fmt.Printf("Flashing device over serial on port '%s' ...\n", port) + return runFirmwareToolWithConfig(ctx, sdk, envelopeFile.Name(), config, flashArguments...) + }) }, } cmd.Flags().StringP("port", "p", ConfiguredPort(), "serial port to flash via") cmd.Flags().Uint("baud", 921600, "baud rate used for the serial flashing") - cmd.Flags().StringP("chip", "c", "esp32", "chip of the target device") - cmd.Flags().String("wifi-ssid", "", "default WiFi network name") - cmd.Flags().String("wifi-password", "", "default WiFi password") - cmd.Flags().String("name", "", "name for the device, if not set a name will be auto generated") - cmd.Flags().Bool("exclude-jaguar", false, "don't install the Jaguar service") cmd.Flags().Bool("skip-port-check", false, "accept the given port without checking") - cmd.Flags().Int("uart-endpoint-rx", -1, "add a UART endpoint to the device listening on the given pin") - cmd.Flags().MarkHidden("uart-endpoint-rx") - cmd.Flags().Uint("uart-endpoint-baud", 0, "set the baud rate for the UART endpoint") - cmd.Flags().MarkHidden("uart-endpoint-baud") + addFirmwareFlashFlags(cmd, "esp32", "name for the device, if not set a name will be auto generated") return cmd }