Skip to content

Commit bf44862

Browse files
Merge pull request #23 from actionforge/actfile-schema
Actfile schema
2 parents d8cead4 + 1f8af81 commit bf44862

11 files changed

Lines changed: 1336 additions & 4 deletions

actfile-schema.json

Lines changed: 475 additions & 0 deletions
Large diffs are not rendered by default.

cmd/cmd_validate.go

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
package cmd
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"os"
67
"os/user"
78
"strings"
89

910
"github.com/actionforge/actrun-cli/core"
1011
u "github.com/actionforge/actrun-cli/utils"
12+
"github.com/santhosh-tekuri/jsonschema/v6"
1113
"github.com/spf13/cobra"
1214
"go.yaml.in/yaml/v4"
1315
)
1416

17+
// ActfileSchema holds the embedded JSON schema bytes, set from main.
18+
var ActfileSchema []byte
19+
1520
var cmdValidate = &cobra.Command{
1621
Use: "validate [graph-file]",
1722
Short: "Validate a graph file.",
@@ -38,6 +43,56 @@ var cmdValidate = &cobra.Command{
3843
},
3944
}
4045

46+
func validateSchema(data any) error {
47+
if len(ActfileSchema) == 0 {
48+
return fmt.Errorf("actfile schema not loaded")
49+
}
50+
51+
var schemaObj any
52+
if err := json.Unmarshal(ActfileSchema, &schemaObj); err != nil {
53+
return fmt.Errorf("failed to parse schema JSON: %w", err)
54+
}
55+
56+
compiler := jsonschema.NewCompiler()
57+
if err := compiler.AddResource("actfile-schema.json", schemaObj); err != nil {
58+
return fmt.Errorf("failed to add schema resource: %w", err)
59+
}
60+
61+
schema, err := compiler.Compile("actfile-schema.json")
62+
if err != nil {
63+
return fmt.Errorf("failed to compile schema: %w", err)
64+
}
65+
66+
return schema.Validate(convertToJSONCompatible(data))
67+
}
68+
69+
// convertToJSONCompatible recursively converts YAML-unmarshalled data into
70+
// types that the JSON schema validator accepts.
71+
func convertToJSONCompatible(v any) any {
72+
switch val := v.(type) {
73+
case map[string]any:
74+
result := make(map[string]any, len(val))
75+
for k, v := range val {
76+
result[k] = convertToJSONCompatible(v)
77+
}
78+
return result
79+
case []any:
80+
result := make([]any, len(val))
81+
for i, v := range val {
82+
result[i] = convertToJSONCompatible(v)
83+
}
84+
return result
85+
case int:
86+
return float64(val)
87+
case int64:
88+
return float64(val)
89+
case float32:
90+
return float64(val)
91+
default:
92+
return val
93+
}
94+
}
95+
4196
func validateGraph(filePath string) error {
4297
fmt.Printf("Validating '%s'...\n", filePath)
4398

@@ -54,10 +109,17 @@ func validateGraph(filePath string) error {
54109
return err
55110
}
56111

112+
hasErrors := false
113+
114+
if err := validateSchema(graphYaml); err != nil {
115+
fmt.Printf("\n❌ Graph schema validation failed:\n%v\n", err)
116+
hasErrors = true
117+
}
118+
57119
_, errs := core.LoadGraph(graphYaml, nil, "", true, core.RunOpts{})
58120

59121
if len(errs) > 0 {
60-
fmt.Printf("\nValidation failed with %d error(s):\n", len(errs))
122+
fmt.Printf("\nGraph validation failed with %d error(s):\n", len(errs))
61123

62124
for i, e := range errs {
63125
if leafErr, ok := e.(*core.LeafError); ok {
@@ -67,6 +129,10 @@ func validateGraph(filePath string) error {
67129
fmt.Printf("\n%d. %v\n", i+1, e)
68130
}
69131
}
132+
hasErrors = true
133+
}
134+
135+
if hasErrors {
70136
return fmt.Errorf("validation failed")
71137
}
72138

@@ -84,6 +150,17 @@ func expandPath(path string) string {
84150
return os.ExpandEnv(path)
85151
}
86152

153+
var cmdSchema = &cobra.Command{
154+
Use: "schema",
155+
Short: "Print the JSON schema for .act files.",
156+
Long: `Prints the JSON schema used to validate ActionForge graph (.act) files.`,
157+
Args: cobra.NoArgs,
158+
Run: func(cmd *cobra.Command, args []string) {
159+
fmt.Println(string(ActfileSchema))
160+
},
161+
}
162+
87163
func init() {
88164
cmdRoot.AddCommand(cmdValidate)
165+
cmdRoot.AddCommand(cmdSchema)
89166
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ require (
2222
github.com/pkg/errors v0.9.1
2323
github.com/rhysd/actionlint v1.7.10
2424
github.com/rossmacarthur/cases v0.3.0
25+
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2
2526
github.com/sirupsen/logrus v1.9.3
2627
github.com/spf13/cobra v1.10.2
2728
github.com/spf13/pflag v1.0.10

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7
231231
github.com/rossmacarthur/cases v0.3.0 h1:7rlsXK2qHb6mQUOX+/IGDO9YyFXqjiDiMpkHfIl54Yg=
232232
github.com/rossmacarthur/cases v0.3.0/go.mod h1:ebnckUNBu5QAJGxFNai/H0IN133rLze6gmoxJyYvHW0=
233233
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
234+
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
235+
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
234236
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
235237
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
236238
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=

main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package main
22

33
import (
4+
_ "embed"
5+
46
_ "github.com/actionforge/actrun-cli/api"
57
"github.com/actionforge/actrun-cli/cmd"
68
_ "github.com/actionforge/actrun-cli/cmd"
@@ -9,6 +11,9 @@ import (
911
"github.com/actionforge/actrun-cli/utils"
1012
)
1113

14+
//go:embed actfile-schema.json
15+
var actfileSchema []byte
16+
1217
func main() {
1318
utils.ApplyLogLevel()
1419

@@ -23,5 +28,6 @@ func main() {
2328
return
2429
}
2530

31+
cmd.ActfileSchema = actfileSchema
2632
cmd.Execute()
2733
}

tests_e2e/references/reference_app.sh_l10

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Usage:
77
Available Commands:
88
completion Generate the autocompletion script for the specified shell
99
help Help about any command
10+
schema Print the JSON schema for .act files.
1011
validate Validate a graph file.
1112
version Print the version number of actrun
1213

tests_e2e/references/reference_app.sh_l35

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ looking for value: 'graph_file'
33
no value (is optional) found for: 'graph_file'
44
Validating '[REDACTED]/missing-exec-connection1.act'...
55

6-
Validation failed with 1 error(s):
6+
Graph validation failed with 1 error(s):
77

88
--- Error 1 ---
99
error:

tests_e2e/references/reference_app.sh_l36

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ looking for value: 'graph_file'
33
no value (is optional) found for: 'graph_file'
44
Validating '[REDACTED]/missing-exec-connection2.act'...
55

6-
Validation failed with 2 error(s):
6+
Graph validation failed with 2 error(s):
77

88
--- Error 1 ---
99
error:

tests_e2e/references/reference_contexts_env.sh_l26

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Usage:
77
Available Commands:
88
completion Generate the autocompletion script for the specified shell
99
help Help about any command
10+
schema Print the JSON schema for .act files.
1011
validate Validate a graph file.
1112
version Print the version number of actrun
1213

tests_e2e/references/reference_validate.sh_l8

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ looking for value: 'graph_file'
44
evaluated to: 'validate.act'
55
Validating 'validate.act'...
66

7-
Validation failed with 4 error(s):
7+
Graph validation failed with 4 error(s):
88

99
--- Error 1 ---
1010
error:

0 commit comments

Comments
 (0)