Skip to content

Commit

Permalink
Do not fully rewrite sketch.yaml when a key is automatically updated
Browse files Browse the repository at this point in the history
  • Loading branch information
cmaglie committed Nov 7, 2022
1 parent f73f660 commit 1a798d8
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 9 deletions.
6 changes: 0 additions & 6 deletions arduino/sketch/profiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,3 @@ func LoadProjectFile(file *paths.Path) (*Project, error) {
}
return res, nil
}

// SaveProjectFile save the sketch project to a file
func (s *Sketch) SaveProjectFile() error {
projectFile := s.GetProjectPath()
return projectFile.WriteFile([]byte(s.Project.AsYaml()))
}
7 changes: 5 additions & 2 deletions arduino/sketch/sketch.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,15 +252,18 @@ func (s *Sketch) GetDefaultPortAddressAndProtocol() (string, string) {
// SetDefaultFQBN sets the default FQBN for the sketch and saves it in the sketch.yaml project file.
func (s *Sketch) SetDefaultFQBN(fqbn string) error {
s.Project.DefaultFqbn = fqbn
return s.SaveProjectFile()
return updateOrAddYamlRootEntry(s.GetProjectPath(), "default_fqbn", fqbn)
}

// SetDefaultPort sets the default port address and port protocol for the sketch and saves it in the
// sketch.yaml project file.
func (s *Sketch) SetDefaultPort(address, protocol string) error {
s.Project.DefaultPort = address
s.Project.DefaultProtocol = protocol
return s.SaveProjectFile()
if err := updateOrAddYamlRootEntry(s.GetProjectPath(), "default_port", address); err != nil {
return err
}
return updateOrAddYamlRootEntry(s.GetProjectPath(), "default_protocol", protocol)
}

// InvalidSketchFolderNameError is returned when the sketch directory doesn't match the sketch name
Expand Down
68 changes: 68 additions & 0 deletions arduino/sketch/yaml.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// This file is part of arduino-cli.
//
// Copyright 2020-2022 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-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 sketch

import (
"fmt"
"strings"

"github.com/arduino/go-paths-helper"
"gopkg.in/yaml.v3"
)

func updateOrAddYamlRootEntry(path *paths.Path, key, newValue string) error {
var srcYaml []string
if path.Exist() {
src, err := path.ReadFileAsLines()
if err != nil {
return err
}
srcYaml = src
}

// Generate the new yaml key/value pair
v, err := yaml.Marshal(newValue)
if err != nil {
return err
}
updatedLine := key + ": " + strings.TrimSpace(string(v))

// Update or add the key/value pair into the original yaml
addMissing := true
for i, line := range srcYaml {
if strings.HasPrefix(line, key+": ") {
srcYaml[i] = updatedLine
addMissing = false
break
}
}
if addMissing {
srcYaml = append(srcYaml, updatedLine)
}

// Validate the new yaml
dstYaml := []byte(strings.Join(srcYaml, fmt.Sprintln()))
var dst interface{}
if err := yaml.Unmarshal(dstYaml, &dst); err != nil {
return fmt.Errorf("%s: %w", tr("could not update sketch project file"), err)
}
if dstMap, ok := dst.(map[string]interface{}); !ok || dstMap[key] != newValue {
return fmt.Errorf(tr("could not update sketch project file"))
}

// Write back the updated YAML
return path.WriteFile(dstYaml)
}
78 changes: 78 additions & 0 deletions arduino/sketch/yaml_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// This file is part of arduino-cli.
//
// Copyright 2020-2022 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-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 sketch

import (
"fmt"
"strings"
"testing"

"github.com/arduino/go-paths-helper"
"github.com/stretchr/testify/require"
)

func TestYamlUpdate(t *testing.T) {
{
sample, err := paths.New("testdata", "SketchWithProfiles", "sketch.yml").ReadFile()
require.NoError(t, err)
tmp, err := paths.WriteToTempFile(sample, nil, "")
require.NoError(t, err)
defer tmp.Remove()

err = updateOrAddYamlRootEntry(tmp, "default_fqbn", "arduino:avr:uno")
require.NoError(t, err)
err = updateOrAddYamlRootEntry(tmp, "default_port", "/dev/ttyACM0")
require.NoError(t, err)

updated, err := tmp.ReadFile()
require.NoError(t, err)
expected := string(sample) + fmt.Sprintln()
expected += fmt.Sprintln("default_fqbn: arduino:avr:uno")
expected += "default_port: /dev/ttyACM0"
require.Equal(t, expected, string(updated))
}
{
sample, err := paths.New("testdata", "SketchWithDefaultFQBNAndPort", "sketch.yml").ReadFile()
require.NoError(t, err)
tmp, err := paths.WriteToTempFile(sample, nil, "")
require.NoError(t, err)
defer tmp.Remove()

err = updateOrAddYamlRootEntry(tmp, "default_fqbn", "TEST1")
require.NoError(t, err)
err = updateOrAddYamlRootEntry(tmp, "default_port", "TEST2")
require.NoError(t, err)

updated, err := tmp.ReadFile()
fmt.Print(string(updated))
require.NoError(t, err)
expected := strings.Replace(string(sample), "arduino:avr:uno", "TEST1", 1)
expected = strings.Replace(expected, "/dev/ttyACM0", "TEST2", 1)
require.Equal(t, expected, string(updated))
}
{
tmp, err := paths.WriteToTempFile([]byte{}, nil, "")
require.NoError(t, err)
require.NoError(t, tmp.Remove())
err = updateOrAddYamlRootEntry(tmp, "default_fqbn", "TEST1")
require.NoError(t, err)

updated, err := tmp.ReadFile()
require.NoError(t, err)
expected := "default_fqbn: TEST1"
require.Equal(t, expected, string(updated))
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ require (
require (
github.com/rogpeppe/go-internal v1.3.0
go.bug.st/testifyjson v1.1.1
gopkg.in/yaml.v3 v3.0.1
)

require (
Expand Down Expand Up @@ -89,5 +90,4 @@ require (
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

0 comments on commit 1a798d8

Please sign in to comment.