Skip to content

Commit 0c279db

Browse files
chore: generate JSON Schema from code (#1704)
* chore: generate JSON Schema from code * fix: make postConfig public * fix: fix JSON Schema * fix: fix the schema of exec.when * fix: fix JSON Schema * chore(go): go mod tidy * fix: fix JSON Schema * fix: add description to JSON Schema --------- Co-authored-by: suzuki-shunsuke-app[bot] <91834585+suzuki-shunsuke-app[bot]@users.noreply.github.com>
1 parent b9eae2b commit 0c279db

File tree

7 files changed

+271
-24
lines changed

7 files changed

+271
-24
lines changed

.cmdx.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,7 @@ tasks:
5555
description: yamllint
5656
usage: yamllint
5757
script: 'find . \( -name "*.yml" -o -name "*.yaml" \) -print0 | xargs -0 yamllint -c .yamllint.yml'
58+
- name: js
59+
description: Generate JSON Schema
60+
usage: Generate JSON Schema
61+
script: "go run ./cmd/gen-jsonschema"

.golangci.yml

+1
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ linters:
1818
- varnamelen
1919
- ireturn
2020
- depguard
21+
- tagalign

cmd/gen-jsonschema/main.go

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"log"
7+
"os"
8+
"strings"
9+
10+
"github.com/invopop/jsonschema"
11+
"github.com/suzuki-shunsuke/github-comment/v6/pkg/config"
12+
)
13+
14+
func main() {
15+
if err := core(); err != nil {
16+
log.Fatal(err)
17+
}
18+
}
19+
20+
func core() error {
21+
if err := gen(&config.Config{}, "json-schema/github-comment.json"); err != nil {
22+
return err
23+
}
24+
return nil
25+
}
26+
27+
func gen(input interface{}, p string) error {
28+
f, err := os.Create(p)
29+
if err != nil {
30+
return fmt.Errorf("create a file %s: %w", p, err)
31+
}
32+
defer f.Close()
33+
encoder := json.NewEncoder(f)
34+
encoder.SetIndent("", " ")
35+
s := jsonschema.Reflect(input)
36+
b, err := json.MarshalIndent(s, "", " ")
37+
if err != nil {
38+
return fmt.Errorf("mashal schema as JSON: %w", err)
39+
}
40+
if err := os.WriteFile(p, []byte(strings.ReplaceAll(string(b), "http://json-schema.org", "https://json-schema.org")+"\n"), 0o644); err != nil { //nolint:gosec,mnd
41+
return fmt.Errorf("write JSON Schema to %s: %w", p, err)
42+
}
43+
return nil
44+
}

go.mod

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/Masterminds/sprig/v3 v3.3.0
77
github.com/expr-lang/expr v1.16.9
88
github.com/google/go-github/v68 v68.0.0
9+
github.com/invopop/jsonschema v0.12.0
910
github.com/mattn/go-colorable v0.1.13
1011
github.com/shurcooL/githubv4 v0.0.0-20240429030203-be2daab69064
1112
github.com/sirupsen/logrus v1.9.3
@@ -24,11 +25,14 @@ require (
2425
dario.cat/mergo v1.0.1 // indirect
2526
github.com/Masterminds/goutils v1.1.1 // indirect
2627
github.com/Masterminds/semver/v3 v3.3.0 // indirect
28+
github.com/bahlo/generic-list-go v0.2.0 // indirect
29+
github.com/buger/jsonparser v1.1.1 // indirect
2730
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
2831
github.com/davecgh/go-spew v1.1.1 // indirect
2932
github.com/google/go-querystring v1.1.0 // indirect
3033
github.com/google/uuid v1.6.0 // indirect
3134
github.com/huandu/xstrings v1.5.0 // indirect
35+
github.com/mailru/easyjson v0.7.7 // indirect
3236
github.com/mattn/go-isatty v0.0.16 // indirect
3337
github.com/mitchellh/copystructure v1.2.0 // indirect
3438
github.com/mitchellh/reflectwalk v1.0.2 // indirect
@@ -37,6 +41,7 @@ require (
3741
github.com/shopspring/decimal v1.4.0 // indirect
3842
github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a // indirect
3943
github.com/spf13/cast v1.7.0 // indirect
44+
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
4045
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
4146
golang.org/x/crypto v0.26.0 // indirect
4247
golang.org/x/net v0.23.0 // indirect

go.sum

+11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+
66
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
77
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
88
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
9+
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
10+
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
11+
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
12+
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
913
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
1014
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
1115
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -26,10 +30,15 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
2630
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
2731
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
2832
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
33+
github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI=
34+
github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
35+
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
2936
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
3037
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
3138
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
3239
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
40+
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
41+
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
3342
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
3443
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
3544
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
@@ -68,6 +77,8 @@ github.com/suzuki-shunsuke/go-error-with-exit-code v1.0.0 h1:oVXrrYNGBq4POyITQNW
6877
github.com/suzuki-shunsuke/go-error-with-exit-code v1.0.0/go.mod h1:kDFtLeftDiIUUHXGI3xq5eJ+uAOi50FPrxPENTHktJ0=
6978
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
7079
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
80+
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
81+
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
7182
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
7283
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
7384
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=

json-schema/github-comment.json

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"$id": "https://github.com/suzuki-shunsuke/github-comment/v6/pkg/config/config",
4+
"$ref": "#/$defs/Config",
5+
"$defs": {
6+
"Base": {
7+
"properties": {
8+
"org": {
9+
"type": "string",
10+
"description": "GitHub organization name"
11+
},
12+
"repo": {
13+
"type": "string",
14+
"description": "GitHub repository name"
15+
}
16+
},
17+
"additionalProperties": false,
18+
"type": "object"
19+
},
20+
"Config": {
21+
"properties": {
22+
"base": {
23+
"$ref": "#/$defs/Base",
24+
"description": "Repository where to post comments"
25+
},
26+
"ghe_base_url": {
27+
"type": "string",
28+
"description": "GitHub Enterprise Base URL"
29+
},
30+
"ghe_graphql_endpoint": {
31+
"type": "string",
32+
"description": "GitHub Enterprise GraphQL Endpoint"
33+
},
34+
"vars": {
35+
"type": "object",
36+
"description": "variables to pass to templates"
37+
},
38+
"templates": {
39+
"additionalProperties": {
40+
"type": "string"
41+
},
42+
"type": "object",
43+
"description": "templates"
44+
},
45+
"post": {
46+
"additionalProperties": {
47+
"$ref": "#/$defs/PostConfig"
48+
},
49+
"type": "object",
50+
"description": "configuration for github-comment post command"
51+
},
52+
"exec": {
53+
"additionalProperties": {
54+
"items": {
55+
"$ref": "#/$defs/ExecConfig"
56+
},
57+
"type": "array"
58+
},
59+
"type": "object",
60+
"description": "configuration for github-comment exec command"
61+
},
62+
"hide": {
63+
"additionalProperties": {
64+
"type": "string"
65+
},
66+
"type": "object",
67+
"description": "configuration for github-comment hide command"
68+
},
69+
"skip_no_token": {
70+
"type": "boolean",
71+
"description": "Skip to post comments if no GitHub access token is passed"
72+
},
73+
"silent": {
74+
"type": "boolean"
75+
}
76+
},
77+
"additionalProperties": false,
78+
"type": "object"
79+
},
80+
"ExecConfig": {
81+
"properties": {
82+
"when": {
83+
"oneOf": [
84+
{
85+
"type": "string"
86+
},
87+
{
88+
"type": "boolean"
89+
}
90+
]
91+
},
92+
"template": {
93+
"type": "string",
94+
"description": "Comment template"
95+
},
96+
"template_for_too_long": {
97+
"type": "string"
98+
},
99+
"dont_comment": {
100+
"type": "boolean",
101+
"description": "Don't post a comment"
102+
},
103+
"embedded_var_names": {
104+
"items": {
105+
"type": "string"
106+
},
107+
"type": "array",
108+
"description": "Embedded variable names"
109+
}
110+
},
111+
"additionalProperties": false,
112+
"type": "object",
113+
"required": [
114+
"when"
115+
]
116+
},
117+
"PostConfig": {
118+
"oneOf": [
119+
{
120+
"type": "string",
121+
"deprecated": true
122+
},
123+
{
124+
"properties": {
125+
"template": {
126+
"type": "string",
127+
"description": "Comment template"
128+
},
129+
"template_for_too_long": {
130+
"type": "string"
131+
},
132+
"embedded_var_names": {
133+
"items": {
134+
"type": "string"
135+
},
136+
"type": "array",
137+
"description": "Embedded variable names"
138+
},
139+
"update": {
140+
"type": "string",
141+
"description": "Update comments that matches with the condition"
142+
}
143+
},
144+
"additionalProperties": false,
145+
"type": "object",
146+
"required": [
147+
"template"
148+
]
149+
}
150+
]
151+
}
152+
}
153+
}

pkg/config/config.go

+53-24
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,51 @@ import (
55
"os"
66
"path/filepath"
77

8+
"github.com/invopop/jsonschema"
89
"gopkg.in/yaml.v2"
910
)
1011

1112
type Config struct {
12-
Base *Base
13-
GHEBaseURL string `yaml:"ghe_base_url"`
14-
GHEGraphQLEndpoint string `yaml:"ghe_graphql_endpoint"`
15-
Vars map[string]interface{}
16-
Templates map[string]string
17-
Post map[string]*PostConfig
18-
Exec map[string][]*ExecConfig
19-
Hide map[string]string
20-
SkipNoToken bool `yaml:"skip_no_token"`
21-
Silent bool
13+
Base *Base `json:"base,omitempty" jsonschema:"description=Repository where to post comments"`
14+
GHEBaseURL string `json:"ghe_base_url,omitempty" yaml:"ghe_base_url" jsonschema:"description=GitHub Enterprise Base URL"`
15+
GHEGraphQLEndpoint string `json:"ghe_graphql_endpoint,omitempty" yaml:"ghe_graphql_endpoint" jsonschema:"description=GitHub Enterprise GraphQL Endpoint"`
16+
Vars map[string]any `json:"vars,omitempty" jsonschema:"description=variables to pass to templates"`
17+
Templates map[string]string `json:"templates,omitempty" jsonschema:"description=templates"`
18+
Post map[string]*PostConfig `json:"post,omitempty" jsonschema:"description=configuration for github-comment post command"`
19+
Exec map[string][]*ExecConfig `json:"exec,omitempty" jsonschema:"description=configuration for github-comment exec command"`
20+
Hide map[string]string `json:"hide,omitempty" jsonschema:"description=configuration for github-comment hide command"`
21+
SkipNoToken bool `json:"skip_no_token,omitempty" yaml:"skip_no_token" jsonschema:"description=Skip to post comments if no GitHub access token is passed"`
22+
Silent bool `json:"silent,omitempty"`
2223
}
2324

2425
type Base struct {
25-
Org string
26-
Repo string
26+
Org string `json:"org,omitempty" jsonschema:"description=GitHub organization name"`
27+
Repo string `json:"repo,omitempty" jsonschema:"description=GitHub repository name"`
2728
}
2829

29-
type PostConfig struct {
30-
Template string
31-
TemplateForTooLong string
32-
EmbeddedVarNames []string
30+
type PostConfig struct { //nolint:recvcheck
31+
Template string `json:"template" jsonschema:"description=Comment template"`
32+
TemplateForTooLong string `json:"template_for_too_long,omitempty"`
33+
EmbeddedVarNames []string `json:"embedded_var_names,omitempty" jsonschema:"description=Embedded variable names"`
3334
// UpdateCondition Update the comment that matches with the condition.
34-
// If multiple comments match, the latest comment is updated
35-
// If no comment matches, aa new comment is created
36-
UpdateCondition string
35+
// If multiple comments match, the latest comment is updated.
36+
// If no comment matches, a new comment is created.
37+
UpdateCondition string `json:"update,omitempty" jsonschema:"description=Update comments that matches with the condition"`
38+
}
39+
40+
type postConfigForJS PostConfig
41+
42+
func (PostConfig) JSONSchema() *jsonschema.Schema {
43+
a := jsonschema.Reflect(&postConfigForJS{}).Definitions["postConfigForJS"]
44+
return &jsonschema.Schema{
45+
OneOf: []*jsonschema.Schema{
46+
{
47+
Type: "string",
48+
Deprecated: true,
49+
},
50+
a,
51+
},
52+
}
3753
}
3854

3955
func (pc *PostConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { //nolint:cyclop
@@ -88,11 +104,24 @@ func (pc *PostConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { /
88104
}
89105

90106
type ExecConfig struct {
91-
When string
92-
Template string
93-
TemplateForTooLong string `yaml:"template_for_too_long"`
94-
DontComment bool `yaml:"dont_comment"`
95-
EmbeddedVarNames []string `yaml:"embedded_var_names"`
107+
When string `json:"when" jsonschema:"description=Condition that this setting is chosen"`
108+
Template string `json:"template,omitempty" jsonschema:"description=Comment template"`
109+
TemplateForTooLong string `json:"template_for_too_long,omitempty" yaml:"template_for_too_long"`
110+
DontComment bool `json:"dont_comment,omitempty" yaml:"dont_comment" jsonschema:"description=Don't post a comment"`
111+
EmbeddedVarNames []string `json:"embedded_var_names,omitempty" yaml:"embedded_var_names" jsonschema:"description=Embedded variable names"`
112+
}
113+
114+
func (ec ExecConfig) JSONSchemaExtend(schema *jsonschema.Schema) {
115+
schema.Properties.Set("when", &jsonschema.Schema{
116+
OneOf: []*jsonschema.Schema{
117+
{
118+
Type: "string",
119+
},
120+
{
121+
Type: "boolean",
122+
},
123+
},
124+
})
96125
}
97126

98127
type ExistFile func(string) bool

0 commit comments

Comments
 (0)