Skip to content

Commit 0a5ed67

Browse files
committed
feat: Allow nesting of directories for examples
Only looks inside quickstart directory now
1 parent 8ace0dc commit 0a5ed67

File tree

2 files changed

+100
-77
lines changed

2 files changed

+100
-77
lines changed

templates/examples.go

Lines changed: 96 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ import (
44
"archive/tar"
55
"bytes"
66
"embed"
7+
"fmt"
78
"io"
89
"io/fs"
9-
"path"
1010
"path/filepath"
11+
"strings"
1112
"sync"
1213

1314
"github.com/gohugoio/hugo/parser/pageparser"
@@ -26,92 +27,115 @@ var (
2627
)
2728

2829
type Example struct {
29-
ID string `json:"id"`
30-
URL string `json:"url"`
31-
Name string `json:"name"`
32-
Description string `json:"description"`
33-
Markdown string `json:"markdown"`
30+
ID string `json:"id"`
31+
URL string `json:"url"`
32+
Name string `json:"name"`
33+
Description string `json:"description"`
34+
Markdown string `json:"markdown"`
35+
DirectoryPath string `json:"directory_path"`
3436
}
3537

3638
const rootDir = "quickstart"
3739

38-
// List returns all embedded examples.
39-
func List() ([]Example, error) {
40-
var returnError error
41-
parseExamples.Do(func() {
42-
files, err := fs.Sub(files, rootDir)
43-
if err != nil {
44-
returnError = xerrors.Errorf("get example fs: %w", err)
45-
}
40+
// WalkForExamples will walk recursively through the examples directory and call
41+
// exampleDirectory() on each directory that contains a main.tf file.
42+
func WalkForExamples(files fs.FS, rootDir string, exampleDirectory func(path string)) error {
43+
return walkDir(exampleDirectory, files, rootDir)
44+
}
4645

47-
groups, err := fs.ReadDir(files, ".")
48-
if err != nil {
49-
returnError = xerrors.Errorf("read dir: %w", err)
50-
return
51-
}
46+
func walkDir(exampleDirectory func(path string), fileFS fs.FS, path string) error {
47+
file, err := fileFS.Open(path)
48+
if err != nil {
49+
return xerrors.Errorf("open file %q: %w", path, err)
50+
}
51+
info, err := file.Stat()
52+
if err != nil {
53+
return xerrors.Errorf("stat file %q: %w", path, err)
54+
}
5255

53-
var dirs []Example
54-
for _, group := range groups {
55-
if group.IsDir() {
56-
groupDirs, err := fs.Sub(files, filepath.Join(rootDir, group.Name()))
57-
}
56+
if info.IsDir() {
57+
files, err := fs.ReadDir(files, path)
58+
if err != nil {
59+
return xerrors.Errorf("read dir %q: %w", path, err)
5860
}
5961

60-
for true {
61-
for _, dir := range dirs {
62-
if !dir.IsDir() {
63-
continue
64-
}
65-
exampleID := dir.Name()
66-
exampleURL := exampleBasePath + exampleID
67-
// Each one of these is a example!
68-
readme, err := fs.ReadFile(files, path.Join(dir.Name(), "README.md"))
62+
for _, file := range files {
63+
n := file.Name()
64+
if strings.EqualFold(file.Name(), "main.tf") {
65+
// This is an example dir
66+
exampleDirectory(path)
67+
return nil
68+
} else if file.IsDir() {
69+
fmt.Println("walk", filepath.Join(path, file.Name()), n)
70+
err := walkDir(exampleDirectory, fileFS, filepath.Join(path, file.Name()))
6971
if err != nil {
70-
returnError = xerrors.Errorf("example %q does not contain README.md", exampleID)
71-
return
72+
return err
7273
}
74+
}
75+
}
76+
}
77+
return nil
78+
}
7379

74-
frontMatter, err := pageparser.ParseFrontMatterAndContent(bytes.NewReader(readme))
75-
if err != nil {
76-
returnError = xerrors.Errorf("parse example %q front matter: %w", exampleID, err)
77-
return
78-
}
80+
// List returns all embedded examples.
81+
func List() ([]Example, error) {
82+
var returnError error
83+
parseExamples.Do(func() {
84+
err := WalkForExamples(files, rootDir, func(path string) {
85+
exampleID := filepath.Base(path)
86+
exampleURL := exampleBasePath + exampleID
87+
// Each one of these is a example!
88+
readme, err := fs.ReadFile(files, filepath.Join(path, "README.md"))
89+
if err != nil {
90+
returnError = xerrors.Errorf("example %q does not contain README.md", exampleID)
91+
return
92+
}
7993

80-
nameRaw, exists := frontMatter.FrontMatter["name"]
81-
if !exists {
82-
returnError = xerrors.Errorf("example %q front matter does not contain name", exampleID)
83-
return
84-
}
94+
frontMatter, err := pageparser.ParseFrontMatterAndContent(bytes.NewReader(readme))
95+
if err != nil {
96+
returnError = xerrors.Errorf("parse example %q front matter: %w", exampleID, err)
97+
return
98+
}
8599

86-
name, valid := nameRaw.(string)
87-
if !valid {
88-
returnError = xerrors.Errorf("example %q name isn't a string", exampleID)
89-
return
90-
}
100+
nameRaw, exists := frontMatter.FrontMatter["name"]
101+
if !exists {
102+
returnError = xerrors.Errorf("example %q front matter does not contain name", exampleID)
103+
return
104+
}
91105

92-
descriptionRaw, exists := frontMatter.FrontMatter["description"]
93-
if !exists {
94-
returnError = xerrors.Errorf("example %q front matter does not contain name", exampleID)
95-
return
96-
}
106+
name, valid := nameRaw.(string)
107+
if !valid {
108+
returnError = xerrors.Errorf("example %q name isn't a string", exampleID)
109+
return
110+
}
97111

98-
description, valid := descriptionRaw.(string)
99-
if !valid {
100-
returnError = xerrors.Errorf("example %q description isn't a string", exampleID)
101-
return
102-
}
112+
descriptionRaw, exists := frontMatter.FrontMatter["description"]
113+
if !exists {
114+
returnError = xerrors.Errorf("example %q front matter does not contain name", exampleID)
115+
return
116+
}
103117

104-
examples = append(examples, Example{
105-
ID: exampleID,
106-
URL: exampleURL,
107-
Name: name,
108-
Description: description,
109-
Markdown: string(frontMatter.Content),
110-
})
118+
description, valid := descriptionRaw.(string)
119+
if !valid {
120+
returnError = xerrors.Errorf("example %q description isn't a string", exampleID)
121+
return
111122
}
112-
}
113123

124+
examples = append(examples, Example{
125+
ID: exampleID,
126+
URL: exampleURL,
127+
Name: name,
128+
Description: description,
129+
Markdown: string(frontMatter.Content),
130+
DirectoryPath: path,
131+
})
132+
})
133+
if err != nil {
134+
returnError = xerrors.Errorf("walking embedded files: %w", err)
135+
return
136+
}
114137
})
138+
115139
return examples, returnError
116140
}
117141

@@ -125,18 +149,17 @@ func Archive(exampleID string) ([]byte, error) {
125149

126150
var selected Example
127151
for _, example := range examples {
128-
if example.ID != exampleID {
129-
continue
152+
if example.ID == exampleID {
153+
selected = example
154+
break
130155
}
131-
selected = example
132-
break
133156
}
134157

135158
if selected.ID == "" {
136159
return nil, xerrors.Errorf("example with id %q not found", exampleID)
137160
}
138161

139-
exampleFiles, err := fs.Sub(files, path.Join(rootDir, exampleID))
162+
exampleFiles, err := fs.Sub(files, selected.DirectoryPath)
140163
if err != nil {
141164
return nil, xerrors.Errorf("get example fs: %w", err)
142165
}

templates/examples_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"io"
88
"testing"
99

10-
"github.com/coder/coder/templates/examples"
10+
examples "github.com/coder/coder/templates"
1111
"github.com/stretchr/testify/assert"
1212
"github.com/stretchr/testify/require"
1313
)
@@ -34,7 +34,7 @@ func TestTemplate(t *testing.T) {
3434

3535
func TestSubdirs(t *testing.T) {
3636
t.Parallel()
37-
tarData, err := examples.Archive("docker-image-builds")
37+
tarData, err := examples.Archive("docker")
3838
require.NoError(t, err)
3939

4040
tarReader := tar.NewReader(bytes.NewReader(tarData))
@@ -49,6 +49,6 @@ func TestSubdirs(t *testing.T) {
4949
entryPaths[header.Typeflag] = append(entryPaths[header.Typeflag], header.Name)
5050
}
5151

52-
require.Subset(t, entryPaths[tar.TypeDir], []string{"./", "images/"})
53-
require.Subset(t, entryPaths[tar.TypeReg], []string{"README.md", "main.tf", "images/base.Dockerfile"})
52+
require.Subset(t, entryPaths[tar.TypeDir], []string{"./", "build/"})
53+
require.Subset(t, entryPaths[tar.TypeReg], []string{"README.md", "main.tf", "build/Dockerfile"})
5454
}

0 commit comments

Comments
 (0)