Skip to content

Commit fe0b042

Browse files
authored
feat(pkg/boards): get on board image version (#157)
* initial flasher import * implement getOSImageVersion * refactoring * refactoring * code review fix * refactoring test * remove unused parameter * enhance tests * move os_image funcs into new file * remove log * code review fix
1 parent eb79b70 commit fe0b042

File tree

2 files changed

+178
-0
lines changed

2 files changed

+178
-0
lines changed

pkg/board/os_image.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// This file is part of arduino-app-cli.
2+
//
3+
// Copyright 2025 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-app-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to [email protected].
15+
16+
package board
17+
18+
import (
19+
"bufio"
20+
"io"
21+
"log/slog"
22+
"strings"
23+
24+
"github.com/arduino/arduino-app-cli/pkg/board/remote"
25+
)
26+
27+
const R0_IMAGE_VERSION_ID = "20250807-136"
28+
29+
// GetOSImageVersion returns the version of the OS image used in the board.
30+
// It is used by the AppLab to enforce image version compatibility.
31+
func GetOSImageVersion(conn remote.RemoteConn) string {
32+
f, err := conn.ReadFile("/etc/buildinfo")
33+
if err != nil {
34+
slog.Warn("Unable to read buildinfo file", "err", err, "using_default", R0_IMAGE_VERSION_ID)
35+
return R0_IMAGE_VERSION_ID
36+
}
37+
defer f.Close()
38+
39+
if version, ok := parseOSImageVersion(f); ok {
40+
return version
41+
}
42+
slog.Warn("Unable to find OS Image version", "using_default", R0_IMAGE_VERSION_ID)
43+
return R0_IMAGE_VERSION_ID
44+
}
45+
46+
func parseOSImageVersion(r io.Reader) (string, bool) {
47+
scanner := bufio.NewScanner(r)
48+
for scanner.Scan() {
49+
line := strings.TrimSpace(scanner.Text())
50+
51+
key, value, ok := strings.Cut(line, "=")
52+
if !ok || key != "BUILD_ID" {
53+
continue
54+
}
55+
56+
version := strings.TrimSpace(value)
57+
if version != "" {
58+
return version, true
59+
}
60+
}
61+
62+
if err := scanner.Err(); err != nil {
63+
return "", false
64+
}
65+
66+
return "", false
67+
}

pkg/board/os_image_test.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// This file is part of arduino-app-cli.
2+
//
3+
// Copyright 2025 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-app-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to [email protected].
15+
package board
16+
17+
import (
18+
"context"
19+
"io"
20+
"strings"
21+
"testing"
22+
23+
"github.com/stretchr/testify/require"
24+
25+
"github.com/arduino/arduino-app-cli/pkg/board/remote"
26+
)
27+
28+
// implements remote.RemoteConn
29+
type MockRemoteConn struct {
30+
ReadFileFunc func(path string) (io.ReadCloser, error)
31+
}
32+
33+
func (m *MockRemoteConn) ReadFile(path string) (io.ReadCloser, error) {
34+
return m.ReadFileFunc(path)
35+
}
36+
37+
// Empty definitions
38+
func (m *MockRemoteConn) List(path string) ([]remote.FileInfo, error) {
39+
return nil, nil
40+
}
41+
func (m *MockRemoteConn) MkDirAll(path string) error {
42+
return nil
43+
}
44+
func (m *MockRemoteConn) Remove(path string) error {
45+
return nil
46+
}
47+
func (m *MockRemoteConn) Stats(path string) (remote.FileInfo, error) {
48+
return remote.FileInfo{}, nil
49+
}
50+
func (m *MockRemoteConn) WriteFile(data io.Reader, path string) error {
51+
return nil
52+
}
53+
func (m *MockRemoteConn) GetCmd(cmd string, args ...string) remote.Cmder {
54+
return nil
55+
}
56+
func (m *MockRemoteConn) Forward(ctx context.Context, localPort int, remotePort int) error {
57+
return nil
58+
}
59+
func (m *MockRemoteConn) ForwardKillAll(ctx context.Context) error {
60+
return nil
61+
}
62+
func createBuildInfoConnection(imageVersion string) remote.RemoteConn {
63+
mockConn := MockRemoteConn{
64+
ReadFileFunc: func(path string) (io.ReadCloser, error) {
65+
return io.NopCloser(strings.NewReader(imageVersion)), nil
66+
},
67+
}
68+
return &mockConn
69+
}
70+
71+
func TestParseOSImageVersion(t *testing.T) {
72+
tests := []struct {
73+
name string
74+
input string
75+
expected string
76+
found bool
77+
}{
78+
{
79+
name: "valid build id",
80+
input: "BUILD_ID=20251006-395\nVARIANT_ID=xfce",
81+
expected: "20251006-395",
82+
found: true,
83+
},
84+
{
85+
name: "missing build id",
86+
input: "VARIANT_ID=xfce\n",
87+
found: false,
88+
},
89+
{
90+
name: "empty build id",
91+
input: "BUILD_ID=\n",
92+
found: false,
93+
},
94+
}
95+
for _, tt := range tests {
96+
t.Run(tt.name, func(t *testing.T) {
97+
got, ok := parseOSImageVersion(strings.NewReader(tt.input))
98+
if ok != tt.found || got != tt.expected {
99+
t.Fatalf("got (%q, %v), expected (%q, %v)", got, ok, tt.expected, tt.found)
100+
}
101+
})
102+
}
103+
}
104+
105+
func TestGetOSImageVersion(t *testing.T) {
106+
const R0_IMAGE_VERSION_ID = "20250807-136"
107+
R0Version := createBuildInfoConnection(R0_IMAGE_VERSION_ID)
108+
AnotherVersion := createBuildInfoConnection("BUILD_ID=20250101-001")
109+
require.Equal(t, GetOSImageVersion(R0Version), R0_IMAGE_VERSION_ID)
110+
require.Equal(t, GetOSImageVersion(AnotherVersion), "20250101-001")
111+
}

0 commit comments

Comments
 (0)