Skip to content

Commit 0aa88da

Browse files
committed
Fix library verification when installing from git repo or zip file
1 parent 56bfccd commit 0aa88da

File tree

5 files changed

+118
-45
lines changed

5 files changed

+118
-45
lines changed

arduino/libraries/librariesmanager/install.go

+44-16
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"fmt"
2121
"net/url"
2222
"os"
23+
"regexp"
2324
"strings"
2425

2526
"github.com/arduino/arduino-cli/arduino/libraries"
@@ -139,7 +140,7 @@ func (lm *LibrariesManager) InstallZipLib(ctx context.Context, archivePath strin
139140
extractionPath := paths[0]
140141
libraryName := extractionPath.Base()
141142

142-
if err := validateLibrary(libraryName, extractionPath); err != nil {
143+
if err := validateLibrary(extractionPath); err != nil {
143144
return err
144145
}
145146

@@ -229,7 +230,7 @@ func (lm *LibrariesManager) InstallGitLib(gitURL string, overwrite bool) error {
229230
return err
230231
}
231232

232-
if err := validateLibrary(libraryName, installPath); err != nil {
233+
if err := validateLibrary(installPath); err != nil {
233234
// Clean up installation directory since this is not a valid library
234235
installPath.RemoveAll()
235236
return err
@@ -259,22 +260,49 @@ func parseGitURL(gitURL string) (string, error) {
259260
return res, nil
260261
}
261262

262-
// validateLibrary verifies the dir contains a valid library, meaning it has both
263-
// an header <name>.h, either in src or root folder, and a library.properties file
264-
func validateLibrary(name string, dir *paths.Path) error {
265-
// Verify library contains library header.
266-
// Checks also root folder for legacy reasons.
267-
// For more info see:
268-
// https://arduino.github.io/arduino-cli/latest/library-specification/#source-code
269-
libraryHeader := name + ".h"
270-
if !dir.Join("src", libraryHeader).Exist() && !dir.Join(libraryHeader).Exist() {
271-
return fmt.Errorf(tr(`library is not valid: missing header file "%s"`), libraryHeader)
263+
// validateLibrary verifies the dir contains a valid library, meaning it has either
264+
// library.properties file and an header in src/ or an header in its root folder.
265+
// Returns nil if dir contains a valid library, error on all other cases.
266+
func validateLibrary(dir *paths.Path) error {
267+
if dir.NotExist() {
268+
return fmt.Errorf(tr("directory doesn't exist: %s", dir))
269+
}
270+
re := regexp.MustCompile(`.hp*$`)
271+
searchHeaderFile := func(d *paths.Path) (bool, error) {
272+
if d.NotExist() {
273+
// A directory that doesn't exist can't obviously contain any header file
274+
return false, nil
275+
}
276+
dirContent, err := d.ReadDir()
277+
if err != nil {
278+
return false, fmt.Errorf(tr("reading directory %s content: %w", dir, err))
279+
}
280+
dirContent.FilterOutDirs()
281+
for _, filePath := range dirContent.AsStrings() {
282+
if re.MatchString(filePath) {
283+
// We found at least one matching file, we're good
284+
return true, nil
285+
}
286+
}
287+
// We didn't find any matching file
288+
return false, nil
272289
}
273290

274-
// Verifies library contains library.properties
275-
if !dir.Join("library.properties").Exist() {
276-
return fmt.Errorf(tr(`library is not valid: missing file "library.properties"`))
291+
// Library format rev2
292+
// https://arduino.github.io/arduino-cli/latest/library-specification/#15-library-format-rev-22
293+
if headerFound, err := searchHeaderFile(dir.Join("src")); err != nil {
294+
return err
295+
} else if dir.Join("library.properties").Exist() && headerFound {
296+
return nil
277297
}
278298

279-
return nil
299+
// Legacy library format
300+
// https://arduino.github.io/arduino-cli/latest/library-specification/#old-library-format-pre-15
301+
if headerFound, err := searchHeaderFile(dir); err != nil {
302+
return err
303+
} else if headerFound {
304+
return nil
305+
}
306+
307+
return fmt.Errorf(tr("library not valid"))
280308
}

arduino/libraries/librariesmanager/install_test.go

+41
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package librariesmanager
1818
import (
1919
"testing"
2020

21+
"github.com/arduino/go-paths-helper"
2122
"github.com/stretchr/testify/require"
2223
)
2324

@@ -62,3 +63,43 @@ func TestParseGitURL(t *testing.T) {
6263
require.Equal(t, "arduino-lib", libraryName)
6364
require.NoError(t, err)
6465
}
66+
67+
func TestValidateLibrary(t *testing.T) {
68+
tmpDir := paths.New(t.TempDir())
69+
70+
nonExistingDirLib := tmpDir.Join("nonExistingDirLib")
71+
err := validateLibrary(nonExistingDirLib)
72+
require.Errorf(t, err, "directory doesn't exist: %s", nonExistingDirLib)
73+
74+
emptyLib := tmpDir.Join("emptyLib")
75+
emptyLib.Mkdir()
76+
err = validateLibrary(emptyLib)
77+
require.Errorf(t, err, "library not valid")
78+
79+
onlyPropertiesLib := tmpDir.Join("onlyPropertiesLib")
80+
onlyPropertiesLib.Mkdir()
81+
onlyPropertiesLib.Join("library.properties").WriteFile([]byte{})
82+
err = validateLibrary(onlyPropertiesLib)
83+
require.Errorf(t, err, "library not valid")
84+
85+
onlyHeaderLib := tmpDir.Join("onlyHeaderLib")
86+
onlyHeaderLibSourceDir := onlyHeaderLib.Join("src")
87+
onlyHeaderLibSourceDir.MkdirAll()
88+
onlyHeaderLibSourceDir.Join("some_file.hpp").WriteFile([]byte{})
89+
err = validateLibrary(onlyHeaderLib)
90+
require.Errorf(t, err, "library not valid")
91+
92+
validLib := tmpDir.Join("valiLib")
93+
validLibSourceDir := validLib.Join("src")
94+
validLibSourceDir.MkdirAll()
95+
validLibSourceDir.Join("some_file.hpp").WriteFile([]byte{})
96+
validLib.Join("library.properties").WriteFile([]byte{})
97+
err = validateLibrary(validLib)
98+
require.NoError(t, err)
99+
100+
validLegacyLib := tmpDir.Join("validLegacyLib")
101+
validLegacyLib.Mkdir()
102+
validLegacyLib.Join("some_file.hpp").WriteFile([]byte{})
103+
err = validateLibrary(validLib)
104+
require.NoError(t, err)
105+
}

i18n/data/en.po

+25-21
Original file line numberDiff line numberDiff line change
@@ -2238,10 +2238,10 @@ msgstr "Use the specified programmer to upload."
22382238
msgid "Used: {0}"
22392239
msgstr "Used: {0}"
22402240

2241-
#: arduino/libraries/librariesmanager/install.go:67
2242-
#: arduino/libraries/librariesmanager/install.go:83
2243-
#: arduino/libraries/librariesmanager/install.go:105
2244-
#: arduino/libraries/librariesmanager/install.go:189
2241+
#: arduino/libraries/librariesmanager/install.go:68
2242+
#: arduino/libraries/librariesmanager/install.go:84
2243+
#: arduino/libraries/librariesmanager/install.go:106
2244+
#: arduino/libraries/librariesmanager/install.go:190
22452245
msgid "User directory not set"
22462246
msgstr "User directory not set"
22472247

@@ -2355,7 +2355,7 @@ msgstr "Writing config file: %v"
23552355
msgid "archive hash differs from hash in index"
23562356
msgstr "archive hash differs from hash in index"
23572357

2358-
#: arduino/libraries/librariesmanager/install.go:136
2358+
#: arduino/libraries/librariesmanager/install.go:137
23592359
msgid "archive is not valid: multiple files found in zip file top level"
23602360
msgstr "archive is not valid: multiple files found in zip file top level"
23612361

@@ -2533,10 +2533,14 @@ msgstr "dependency '%s' is not available"
25332533
msgid "destination already exists"
25342534
msgstr "destination already exists"
25352535

2536-
#: arduino/libraries/librariesmanager/install.go:74
2536+
#: arduino/libraries/librariesmanager/install.go:75
25372537
msgid "destination dir %s already exists, cannot install"
25382538
msgstr "destination dir %s already exists, cannot install"
25392539

2540+
#: arduino/libraries/librariesmanager/install.go:268
2541+
msgid "directory doesn't exist: %s"
2542+
msgstr "directory doesn't exist: %s"
2543+
25402544
#: arduino/discovery/discoverymanager/discoverymanager.go:112
25412545
msgid "discovery %[1]s process not started: %[2]w"
25422546
msgstr "discovery %[1]s process not started: %[2]w"
@@ -2599,7 +2603,7 @@ msgstr "error: %s and %s flags cannot be used together"
25992603
msgid "extracting archive: %s"
26002604
msgstr "extracting archive: %s"
26012605

2602-
#: arduino/libraries/librariesmanager/install.go:124
2606+
#: arduino/libraries/librariesmanager/install.go:125
26032607
msgid "extracting archive: %w"
26042608
msgstr "extracting archive: %w"
26052609

@@ -2694,7 +2698,7 @@ msgstr "getting tool dependencies for platform %[1]s: %[2]s"
26942698
msgid "importing sketch metadata: %s"
26952699
msgstr "importing sketch metadata: %s"
26962700

2697-
#: arduino/libraries/librariesmanager/install.go:91
2701+
#: arduino/libraries/librariesmanager/install.go:92
26982702
msgid "install directory not set"
26992703
msgstr "install directory not set"
27002704

@@ -2755,7 +2759,7 @@ msgstr "invalid empty library version: %s"
27552759
msgid "invalid empty option found"
27562760
msgstr "invalid empty option found"
27572761

2758-
#: arduino/libraries/librariesmanager/install.go:257
2762+
#: arduino/libraries/librariesmanager/install.go:258
27592763
msgid "invalid git url"
27602764
msgstr "invalid git url"
27612765

@@ -2819,22 +2823,18 @@ msgstr "key not found in settings"
28192823
msgid "keywords"
28202824
msgstr "keywords"
28212825

2822-
#: arduino/libraries/librariesmanager/install.go:162
2823-
#: arduino/libraries/librariesmanager/install.go:205
2826+
#: arduino/libraries/librariesmanager/install.go:163
2827+
#: arduino/libraries/librariesmanager/install.go:206
28242828
msgid "library %s already installed"
28252829
msgstr "library %s already installed"
28262830

2827-
#: arduino/libraries/librariesmanager/install.go:37
2831+
#: arduino/libraries/librariesmanager/install.go:38
28282832
msgid "library already installed"
28292833
msgstr "library already installed"
28302834

2831-
#: arduino/libraries/librariesmanager/install.go:276
2832-
msgid "library is not valid: missing file \"library.properties\""
2833-
msgstr "library is not valid: missing file \"library.properties\""
2834-
2835-
#: arduino/libraries/librariesmanager/install.go:271
2836-
msgid "library is not valid: missing header file \"%s\""
2837-
msgstr "library is not valid: missing header file \"%s\""
2835+
#: arduino/libraries/librariesmanager/install.go:307
2836+
msgid "library not valid"
2837+
msgstr "library not valid"
28382838

28392839
#: arduino/libraries/librariesmanager/librariesmanager.go:226
28402840
msgid "library path does not exist: %s"
@@ -2917,7 +2917,7 @@ msgstr "missing platform release %[1]s:%[2]s referenced by board %[3]s"
29172917
msgid "monitor release not found: %s"
29182918
msgstr "monitor release not found: %s"
29192919

2920-
#: arduino/libraries/librariesmanager/install.go:179
2920+
#: arduino/libraries/librariesmanager/install.go:180
29212921
#: arduino/resources/install.go:94
29222922
msgid "moving extracted archive to destination dir: %s"
29232923
msgstr "moving extracted archive to destination dir: %s"
@@ -3077,6 +3077,10 @@ msgstr "reading dir %[1]s: %[2]s"
30773077
msgid "reading directory %[1]s: %[2]s"
30783078
msgstr "reading directory %[1]s: %[2]s"
30793079

3080+
#: arduino/libraries/librariesmanager/install.go:278
3081+
msgid "reading directory %s content: %w"
3082+
msgstr "reading directory %s content: %w"
3083+
30803084
#: arduino/builder/sketch.go:76
30813085
msgid "reading file %[1]s: %[2]s"
30823086
msgstr "reading file %[1]s: %[2]s"
@@ -3135,7 +3139,7 @@ msgstr "release not found"
31353139
msgid "removing corrupted archive file: %s"
31363140
msgstr "removing corrupted archive file: %s"
31373141

3138-
#: arduino/libraries/librariesmanager/install.go:94
3142+
#: arduino/libraries/librariesmanager/install.go:95
31393143
msgid "removing lib directory: %s"
31403144
msgstr "removing lib directory: %s"
31413145

i18n/rice-box.go

+4-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/test_lib.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -925,7 +925,7 @@ def test_install_zip_invalid_library(run_command, data_dir, downloads_dir):
925925
# Test zip-path install
926926
res = run_command(["lib", "install", "--zip-path", zip_path])
927927
assert res.failed
928-
assert 'library is not valid: missing header file "lib-without-header.h"' in res.stderr
928+
assert 'library not valid' in res.stderr
929929

930930
lib_install_dir = Path(data_dir, "libraries", "lib-without-properties")
931931
# Verifies library is not already installed
@@ -935,7 +935,7 @@ def test_install_zip_invalid_library(run_command, data_dir, downloads_dir):
935935
# Test zip-path install
936936
res = run_command(["lib", "install", "--zip-path", zip_path])
937937
assert res.failed
938-
assert 'library is not valid: missing file "library.properties"' in res.stderr
938+
assert 'library not valid' in res.stderr
939939

940940

941941
def test_install_git_invalid_library(run_command, data_dir, downloads_dir):
@@ -962,7 +962,7 @@ def test_install_git_invalid_library(run_command, data_dir, downloads_dir):
962962

963963
res = run_command(["lib", "install", "--git-url", repo_dir], custom_env=env)
964964
assert res.failed
965-
assert 'library is not valid: missing header file "lib-without-header.h"' in res.stderr
965+
assert 'library not valid' in res.stderr
966966
assert not lib_install_dir.exists()
967967

968968
# Create another fake library repository
@@ -980,5 +980,5 @@ def test_install_git_invalid_library(run_command, data_dir, downloads_dir):
980980

981981
res = run_command(["lib", "install", "--git-url", repo_dir], custom_env=env)
982982
assert res.failed
983-
assert 'library is not valid: missing file "library.properties"' in res.stderr
983+
assert 'library not valid' in res.stderr
984984
assert not lib_install_dir.exists()

0 commit comments

Comments
 (0)