Skip to content

Commit 18809cc

Browse files
Add maestro check-syntax command (#2387)
1 parent ba7e0fb commit 18809cc

File tree

6 files changed

+112
-1
lines changed

6 files changed

+112
-1
lines changed

maestro

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,10 @@
22

33
set -e
44

5-
./gradlew :maestro-cli:installDist -q && ./maestro-cli/build/install/maestro/bin/maestro "$@"
5+
if [ -t 0 ]; then
6+
input=""
7+
else
8+
input=$(cat -)
9+
fi
10+
11+
./gradlew :maestro-cli:installDist -q && echo "$input" | ./maestro-cli/build/install/maestro/bin/maestro "$@"

maestro-cli/src/main/java/maestro/cli/App.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import maestro.MaestroException
2323
import maestro.cli.analytics.Analytics
2424
import maestro.cli.command.BugReportCommand
2525
import maestro.cli.command.ChatCommand
26+
import maestro.cli.command.CheckSyntaxCommand
2627
import maestro.cli.command.CloudCommand
2728
import maestro.cli.command.DownloadSamplesCommand
2829
import maestro.cli.command.LoginCommand
@@ -64,6 +65,7 @@ import kotlin.system.exitProcess
6465
StartDeviceCommand::class,
6566
GenerateCompletion::class,
6667
ChatCommand::class,
68+
CheckSyntaxCommand::class,
6769
]
6870
)
6971
class App {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package maestro.cli.command
2+
3+
import maestro.cli.CliError
4+
import maestro.orchestra.error.SyntaxError
5+
import maestro.orchestra.yaml.YamlCommandReader
6+
import picocli.CommandLine
7+
import java.io.File
8+
import java.util.concurrent.Callable
9+
10+
@CommandLine.Command(
11+
name = "check-syntax",
12+
description = [
13+
"Check syntax of Maestro code"
14+
],
15+
hidden = true
16+
)
17+
class CheckSyntaxCommand : Callable<Int> {
18+
19+
@CommandLine.Parameters(
20+
index = "0",
21+
description = ["Check syntax of Maestro flow file or \"-\" for stdin"],
22+
)
23+
private lateinit var file: File
24+
25+
override fun call(): Int {
26+
val maestroCode = if (file.path == "-") {
27+
System.`in`.readBytes().toString(Charsets.UTF_8)
28+
} else {
29+
if (!file.exists()) throw CliError("File does not exist: ${file.absolutePath}")
30+
file.readText()
31+
}
32+
if (maestroCode.isBlank()) throw CliError("Maestro code is empty.")
33+
try {
34+
YamlCommandReader.checkSyntax(maestroCode)
35+
println("OK")
36+
} catch (e: SyntaxError) {
37+
throw CliError(e.message)
38+
}
39+
return 0
40+
}
41+
}

maestro-orchestra/src/main/java/maestro/orchestra/yaml/MaestroFlowParser.kt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.core.JsonLocation
2323
import com.fasterxml.jackson.core.JsonParser
2424
import com.fasterxml.jackson.core.JsonProcessingException
2525
import com.fasterxml.jackson.core.JsonToken
26+
import com.fasterxml.jackson.core.TreeNode
2627
import com.fasterxml.jackson.databind.DeserializationContext
2728
import com.fasterxml.jackson.databind.JsonDeserializer
2829
import com.fasterxml.jackson.databind.ObjectMapper
@@ -40,6 +41,7 @@ import maestro.orchestra.error.MediaFileNotFound
4041
import maestro.orchestra.util.Env.withEnv
4142
import org.intellij.lang.annotations.Language
4243
import java.nio.file.Path
44+
import java.nio.file.Paths
4345
import kotlin.io.path.absolute
4446
import kotlin.io.path.isDirectory
4547
import kotlin.io.path.readText
@@ -486,6 +488,50 @@ object MaestroFlowParser {
486488
return MAPPER.writeValueAsString(commands.map { MAPPER.readTree(it) })
487489
}
488490

491+
fun checkSyntax(maestroCode: String) {
492+
MAPPER.createParser(maestroCode).use { parser ->
493+
val node = parser.readValueAsTree<TreeNode>()
494+
if (node.isArray) {
495+
checkCommandListSyntax(maestroCode)
496+
} else if (node.isObject && parser.nextToken() != null) {
497+
checkFlowSyntax(maestroCode)
498+
} else {
499+
checkCommandSyntax(maestroCode)
500+
}
501+
}
502+
}
503+
504+
private fun checkCommandListSyntax(maestroCode: String) {
505+
MAPPER.createParser(maestroCode).use { parser ->
506+
try {
507+
parseCommands(parser)
508+
} catch (e: Throwable) {
509+
throw wrapException(e, parser, Paths.get("/syntax-checker"), maestroCode)
510+
}
511+
}
512+
}
513+
514+
private fun checkCommandSyntax(command: String) {
515+
MAPPER.createParser(command).use { parser ->
516+
try {
517+
parser.readValueAs(YamlFluentCommand::class.java)
518+
} catch (e: Throwable) {
519+
throw wrapException(e, parser, Paths.get("/syntax-checker"), command)
520+
}
521+
}
522+
}
523+
524+
private fun checkFlowSyntax(flow: String) {
525+
MAPPER.createParser(flow).use { parser ->
526+
try {
527+
parseConfig(parser)
528+
parseCommands(parser)
529+
} catch (e: Throwable) {
530+
throw wrapException(e, parser, Paths.get("/syntax-checker"), flow)
531+
}
532+
}
533+
}
534+
489535
private fun parseCommands(parser: JsonParser): List<YamlFluentCommand> {
490536
if (parser.nextToken() != JsonToken.START_ARRAY) {
491537
throw ParseException(

maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlCommandReader.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import maestro.orchestra.WorkspaceConfig
2828
import maestro.orchestra.error.SyntaxError
2929
import maestro.utils.drawTextBox
3030
import java.nio.file.Path
31+
import java.nio.file.Paths
3132
import kotlin.io.path.absolutePathString
3233
import kotlin.io.path.readText
3334

@@ -70,6 +71,10 @@ object YamlCommandReader {
7071

7172
fun formatCommands(commands: List<String>): String = MaestroFlowParser.formatCommands(commands)
7273

74+
fun checkSyntax(maestroCode: String) = mapParsingErrors(Paths.get("/syntax-checker/")) {
75+
MaestroFlowParser.checkSyntax(maestroCode)
76+
}
77+
7378
private fun <T> mapParsingErrors(path: Path, block: () -> T): T {
7479
try {
7580
return block()

tmp.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env bash
2+
3+
set -e
4+
5+
if [ -t 0 ]; then
6+
input=""
7+
else
8+
input=$(cat -)
9+
fi
10+
11+
echo "Hello $input"

0 commit comments

Comments
 (0)