Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd53ceb3

Browse files
committed
feat(examples): add linting to all examples
1 parent4cba83b commitd53ceb3

File tree

2 files changed

+153
-72
lines changed

2 files changed

+153
-72
lines changed

‎Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ else
428428
endif
429429
.PHONY: fmt/shfmt
430430

431-
lint: lint/shellcheck lint/go lint/ts lint/helm lint/site-icons
431+
lint: lint/shellcheck lint/go lint/ts lint/examples lint/helm lint/site-icons
432432
.PHONY: lint
433433

434434
lint/site-icons:
@@ -447,6 +447,10 @@ lint/go:
447447
golangci-lint run
448448
.PHONY: lint/go
449449

450+
lint/examples:
451+
go run ./scripts/examplegen/main.go -lint
452+
.PHONY: lint/examples
453+
450454
# Use shfmt to determine the shell files, takes editorconfig into consideration.
451455
lint/shellcheck:$(SHELL_SRC_FILES)
452456
echo"--- shellcheck"

‎scripts/examplegen/main.go

Lines changed: 148 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ package main
33
import (
44
"bytes"
55
"encoding/json"
6+
"errors"
7+
"flag"
68
"fmt"
79
"go/parser"
810
"go/token"
11+
"io"
912
"io/fs"
1013
"os"
1114
"path"
@@ -24,122 +27,196 @@ const (
2427
)
2528

2629
funcmain() {
27-
iferr:=run();err!=nil {
28-
panic(err)
30+
lint:=flag.Bool("lint",false,"Lint **all** the examples instead of generating the examples.gen.json file")
31+
flag.Parse()
32+
33+
iferr:=run(*lint);err!=nil {
34+
_,_=fmt.Fprintf(os.Stderr,"error: %+v\n",err)
35+
os.Exit(1)
2936
}
3037
}
3138

32-
funcrun()error {
39+
//nolint:revive // This is a script, not a library.
40+
funcrun(lintbool)error {
3341
fset:=token.NewFileSet()
3442
src,err:=parser.ParseFile(fset,filepath.Join(examplesDir,examplesSrc),nil,parser.ParseComments)
3543
iferr!=nil {
3644
returnerr
3745
}
3846

47+
projectFS:=os.DirFS(".")
48+
examplesFS:=os.DirFS(examplesDir)
49+
3950
varpaths []string
40-
for_,comment:=rangesrc.Comments {
41-
for_,line:=rangecomment.List {
42-
ifs,ok:=parseEmbedTag(line.Text);ok&&!strings.HasSuffix(s,".json") {
43-
paths=append(paths,s)
51+
iflint {
52+
files,err:=fs.ReadDir(examplesFS,"templates")
53+
iferr!=nil {
54+
returnerr
55+
}
56+
57+
for_,f:=rangefiles {
58+
if!f.IsDir() {
59+
continue
60+
}
61+
paths=append(paths,filepath.Join("templates",f.Name()))
62+
}
63+
}else {
64+
for_,comment:=rangesrc.Comments {
65+
for_,line:=rangecomment.List {
66+
ifs,ok:=parseEmbedTag(line.Text);ok&&!strings.HasSuffix(s,".json") {
67+
paths=append(paths,s)
68+
}
4469
}
4570
}
4671
}
4772

4873
varexamples []codersdk.TemplateExample
49-
files:=os.DirFS(examplesDir)
74+
varerrs []error
5075
for_,name:=rangepaths {
51-
dir,err:=fs.Stat(files,name)
76+
te,err:=parseTemplateExample(projectFS,examplesFS,name)
5277
iferr!=nil {
53-
returnerr
54-
}
55-
if!dir.IsDir() {
78+
errs=append(errs,err)
5679
continue
5780
}
58-
exampleID:=dir.Name()
59-
// Each one of these is a example!
60-
readme,err:=fs.ReadFile(files,path.Join(name,"README.md"))
61-
iferr!=nil {
62-
returnxerrors.Errorf("example %q does not contain README.md",exampleID)
81+
ifte!=nil {
82+
examples=append(examples,*te)
6383
}
84+
}
85+
86+
iflen(errs)>0 {
87+
returnxerrors.Errorf("parse failed: %w",errors.Join(errs...))
88+
}
89+
90+
varw io.Writer=os.Stdout
91+
iflint {
92+
w=io.Discard
93+
}
6494

65-
frontMatter,err:=pageparser.ParseFrontMatterAndContent(bytes.NewReader(readme))
95+
_,err=fmt.Fprint(w,"// Code generated by examplegen. DO NOT EDIT.\n")
96+
iferr!=nil {
97+
returnerr
98+
}
99+
100+
enc:=json.NewEncoder(os.Stdout)
101+
enc.SetIndent(""," ")
102+
returnenc.Encode(examples)
103+
}
104+
105+
funcparseTemplateExample(projectFS,examplesFS fs.FS,namestring) (te*codersdk.TemplateExample,errerror) {
106+
varerrs []error
107+
deferfunc() {
66108
iferr!=nil {
67-
returnxerrors.Errorf("parse example %q front matter: %w",exampleID,err)
109+
errs=append([]error{err},errs...)
68110
}
69-
70-
nameRaw,exists:=frontMatter.FrontMatter["display_name"]
71-
if!exists {
72-
returnxerrors.Errorf("example %q front matter does not contain name",exampleID)
111+
iflen(errs)>0 {
112+
err=xerrors.Errorf("example %q has errors",name)
113+
for_,e:=rangeerrs {
114+
err=errors.Join(err,e)
115+
}
73116
}
117+
}()
74118

75-
name,valid:=nameRaw.(string)
76-
if!valid {
77-
returnxerrors.Errorf("example %q name isn't a string",exampleID)
78-
}
119+
dir,err:=fs.Stat(examplesFS,name)
120+
iferr!=nil {
121+
returnnil,err
122+
}
123+
if!dir.IsDir() {
124+
returnnil,nil
125+
}
79126

80-
descriptionRaw,exists:=frontMatter.FrontMatter["description"]
81-
if!exists {
82-
returnxerrors.Errorf("example %q front matter does not contain name",exampleID)
83-
}
127+
exampleID:=dir.Name()
128+
// Each one of these is a example!
129+
readme,err:=fs.ReadFile(examplesFS,path.Join(name,"README.md"))
130+
iferr!=nil {
131+
returnnil,xerrors.New("missing README.md")
132+
}
84133

85-
description,valid:=descriptionRaw.(string)
86-
if!valid {
87-
returnxerrors.Errorf("example %q description isn't a string",exampleID)
88-
}
134+
frontMatter,err:=pageparser.ParseFrontMatterAndContent(bytes.NewReader(readme))
135+
iferr!=nil {
136+
returnnil,xerrors.Errorf("parse front matter: %w",err)
137+
}
89138

90-
tags:= []string{}
91-
tagsRaw,exists:=frontMatter.FrontMatter["tags"]
92-
ifexists {
93-
tagsI,valid:=tagsRaw.([]interface{})
94-
if!valid {
95-
returnxerrors.Errorf("example %q tags isn't a slice: type %T",exampleID,tagsRaw)
96-
}
139+
// Make sure validation here is in sync with requirements for
140+
// coder/registry.
141+
displayName,err:=getString(frontMatter.FrontMatter,"display_name")
142+
iferr!=nil {
143+
errs=append(errs,err)
144+
}
145+
146+
description,err:=getString(frontMatter.FrontMatter,"description")
147+
iferr!=nil {
148+
errs=append(errs,err)
149+
}
150+
151+
_,err=getString(frontMatter.FrontMatter,"maintainer_github")
152+
iferr!=nil {
153+
errs=append(errs,err)
154+
}
155+
156+
tags:= []string{}
157+
tagsRaw,exists:=frontMatter.FrontMatter["tags"]
158+
ifexists {
159+
tagsI,valid:=tagsRaw.([]interface{})
160+
if!valid {
161+
errs=append(errs,xerrors.Errorf("tags isn't a slice: type %T",tagsRaw))
162+
}else {
97163
for_,tagI:=rangetagsI {
98164
tag,valid:=tagI.(string)
99165
if!valid {
100-
returnxerrors.Errorf("example %q tag isn't a string: type %T",exampleID,tagI)
166+
errs=append(errs,xerrors.Errorf("tag isn't a string: type %T",tagI))
167+
continue
101168
}
102169
tags=append(tags,tag)
103170
}
104171
}
172+
}
105173

106-
variconstring
107-
iconRaw,exists:=frontMatter.FrontMatter["icon"]
108-
ifexists {
109-
icon,valid=iconRaw.(string)
110-
if!valid {
111-
returnxerrors.Errorf("example %q icon isn't a string",exampleID)
174+
variconstring
175+
iconRaw,exists:=frontMatter.FrontMatter["icon"]
176+
ifexists {
177+
varvalidbool
178+
icon,valid=iconRaw.(string)
179+
if!valid {
180+
errs=append(errs,xerrors.Errorf("icon isn't a string: type %T",iconRaw))
181+
}else {
182+
cleanPath:=filepath.Clean(filepath.Join(examplesDir,name,icon))
183+
_,err:=fs.Stat(projectFS,cleanPath)
184+
iferr!=nil {
185+
errs=append(errs,xerrors.Errorf("icon does not exist: %w",err))
112186
}
113-
icon,err=filepath.Rel("../site/static/",filepath.Join(examplesDir,name,icon))
187+
icon,err=filepath.Rel("site/static/",cleanPath)
114188
iferr!=nil {
115-
returnxerrors.Errorf("example %qicon is not in site/static: %w",exampleID,err)
189+
errs=append(errs,xerrors.Errorf("icon is not in site/static: %w",err))
116190
}
117-
// The FE needs a static path!
118-
icon="/"+icon
119191
}
192+
}
120193

121-
examples=append(examples, codersdk.TemplateExample{
122-
ID:exampleID,
123-
Name:name,
124-
Description:description,
125-
Icon:icon,
126-
Tags:tags,
127-
Markdown:string(frontMatter.Content),
128-
129-
// URL is set by examples/examples.go.
130-
})
194+
iflen(errs)>0 {
195+
returnnil,xerrors.New("front matter validation failed")
131196
}
132197

133-
w:=os.Stdout
198+
return&codersdk.TemplateExample{
199+
ID:exampleID,
200+
Name:displayName,
201+
Description:description,
202+
Icon:"/"+icon,// The FE needs a static path!
203+
Tags:tags,
204+
Markdown:string(frontMatter.Content),
134205

135-
_,err=fmt.Fprint(w,"// Code generated by examplegen. DO NOT EDIT.\n")
136-
iferr!=nil {
137-
returnerr
138-
}
206+
// URL is set by examples/examples.go.
207+
},nil
208+
}
139209

140-
enc:=json.NewEncoder(os.Stdout)
141-
enc.SetIndent(""," ")
142-
returnenc.Encode(examples)
210+
funcgetString(mmap[string]any,keystring) (string,error) {
211+
v,ok:=m[key]
212+
if!ok {
213+
return"",xerrors.Errorf("front matter does not contain %q",key)
214+
}
215+
vv,ok:=v.(string)
216+
if!ok {
217+
return"",xerrors.Errorf("%q isn't a string",key)
218+
}
219+
returnvv,nil
143220
}
144221

145222
funcparseEmbedTag(sstring) (string,bool) {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp