From a71415facd42d9384e4ac3f34a943b5b31ac9b72 Mon Sep 17 00:00:00 2001
From: MatteoPologruto <109663225+MatteoPologruto@users.noreply.github.com>
Date: Tue, 20 Jun 2023 08:38:50 +0200
Subject: [PATCH] [breaking] Check cross-platform compatibility of sketch names
(#2216)
* Exclude sketch names ending with a dot
* Fail with error if a reserved name is used as sketch name
* Update sketch name specifications in docs
---
commands/sketch/new.go | 12 ++++++++++--
commands/sketch/new_test.go | 16 ++++++++++++++--
docs/UPGRADING.md | 7 +++++++
docs/sketch-specification.md | 4 +++-
4 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/commands/sketch/new.go b/commands/sketch/new.go
index ff2828b4e6f..b4fe3ab0e82 100644
--- a/commands/sketch/new.go
+++ b/commands/sketch/new.go
@@ -37,7 +37,10 @@ void loop() {
// sketchNameMaxLength could be part of the regex, but it's intentionally left out for clearer error reporting
var sketchNameMaxLength = 63
-var sketchNameValidationRegex = regexp.MustCompile(`^[0-9a-zA-Z_][0-9a-zA-Z_\.-]*$`)
+var sketchNameValidationRegex = regexp.MustCompile(`^[0-9a-zA-Z_](?:[0-9a-zA-Z_\.-]*[0-9a-zA-Z_-]|)$`)
+
+var invalidNames = []string{"CON", "PRN", "AUX", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5",
+ "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"}
// NewSketch creates a new sketch via gRPC
func NewSketch(ctx context.Context, req *rpc.NewSketchRequest) (*rpc.NewSketchResponse, error) {
@@ -80,8 +83,13 @@ func validateSketchName(name string) error {
sketchNameMaxLength))}
}
if !sketchNameValidationRegex.MatchString(name) {
- return &arduino.CantCreateSketchError{Cause: errors.New(tr(`invalid sketch name "%[1]s": the first character must be alphanumeric or "_", the following ones can also contain "-" and ".".`,
+ return &arduino.CantCreateSketchError{Cause: errors.New(tr(`invalid sketch name "%[1]s": the first character must be alphanumeric or "_", the following ones can also contain "-" and ".". The last one cannot be ".".`,
name))}
}
+ for _, invalid := range invalidNames {
+ if name == invalid {
+ return &arduino.CantCreateSketchError{Cause: errors.New(tr(`sketch name cannot be the reserved name "%[1]s"`, invalid))}
+ }
+ }
return nil
}
diff --git a/commands/sketch/new_test.go b/commands/sketch/new_test.go
index a0812448c6f..00ae7856732 100644
--- a/commands/sketch/new_test.go
+++ b/commands/sketch/new_test.go
@@ -30,6 +30,7 @@ func Test_SketchNameWrongPattern(t *testing.T) {
".hello",
"-hello",
"hello*",
+ "hello.",
"||||||||||||||",
",`hack[}attempt{];",
}
@@ -39,7 +40,7 @@ func Test_SketchNameWrongPattern(t *testing.T) {
SketchDir: t.TempDir(),
})
- require.EqualError(t, err, fmt.Sprintf(`Can't create sketch: invalid sketch name "%s": the first character must be alphanumeric or "_", the following ones can also contain "-" and ".".`,
+ require.EqualError(t, err, fmt.Sprintf(`Can't create sketch: invalid sketch name "%s": the first character must be alphanumeric or "_", the following ones can also contain "-" and ".". The last one cannot be ".".`,
name))
}
}
@@ -78,7 +79,6 @@ func Test_SketchNameOk(t *testing.T) {
"h",
"h.ello",
"h..ello-world",
- "h..ello-world.",
"hello_world__",
"_hello_world",
string(lengthLimitName),
@@ -91,3 +91,15 @@ func Test_SketchNameOk(t *testing.T) {
require.Nil(t, err)
}
}
+
+func Test_SketchNameReserved(t *testing.T) {
+ invalidNames := []string{"CON", "PRN", "AUX", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5",
+ "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"}
+ for _, name := range invalidNames {
+ _, err := NewSketch(context.Background(), &commands.NewSketchRequest{
+ SketchName: name,
+ SketchDir: t.TempDir(),
+ })
+ require.EqualError(t, err, fmt.Sprintf(`Can't create sketch: sketch name cannot be the reserved name "%s"`, name))
+ }
+}
diff --git a/docs/UPGRADING.md b/docs/UPGRADING.md
index 39d0ef5b992..63071bb2f50 100644
--- a/docs/UPGRADING.md
+++ b/docs/UPGRADING.md
@@ -4,6 +4,13 @@ Here you can find a list of migration guides to handle breaking changes between
## 0.34.0
+### Updated sketch name specifications
+
+[Sketch name specifications](https://arduino.github.io/arduino-cli/dev/sketch-specification) have been updated to
+achieve cross-platform compatibility.
+
+Existing sketch names violating the new constraint need to be updated.
+
### golang API: `LoadSketch` function has been moved
The function `github.com/arduino/arduino-cli/commands.LoadSketch` has been moved to package
diff --git a/docs/sketch-specification.md b/docs/sketch-specification.md
index f7488c794ca..316787476c6 100644
--- a/docs/sketch-specification.md
+++ b/docs/sketch-specification.md
@@ -7,7 +7,9 @@ The programs that run on Arduino boards are called "sketches". This term was inh
The sketch root folder name and code file names must start with a basic letter (`A`-`Z` or `a`-`z`), number (`0`-`9`)
[1](#leading-number-note), or underscore (`_`) [2](#leading-underscore-note) followed by basic
-letters, numbers, underscores, dots (`.`) and dashes (`-`). The maximum length is 63 characters.
+letters, numbers, underscores, dots (`.`) and dashes (`-`). The maximum length is 63 characters. The sketch name cannot
+end with a dot (`.`) and cannot be a
+[reserved name](https://learn.microsoft.com/windows/win32/fileio/naming-a-file#naming-conventions).
1 Supported from Arduino IDE 1.8.4.
2 Supported in all versions except Arduino IDE 2.0.4/Arduino CLI