Skip to content

Commit 7b227f9

Browse files
dwahlerkylecarbs
authored andcommitted
fix: include subdirectories in example templates (#1715)
1 parent fa3ca7c commit 7b227f9

File tree

27 files changed

+91
-41
lines changed

27 files changed

+91
-41
lines changed

.github/dependabot.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ updates:
5555
- version-update:semver-major
5656

5757
- package-ecosystem: "terraform"
58-
directory: "/examples"
58+
directory: "/examples/templates"
5959
schedule:
6060
interval: "weekly"
6161
time: "06:00"

docs/about.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ layer of infrastructure control. This additional layer allows admins to:
4848
- Enable persistent workspaces, which are like local machines, but faster and
4949
hosted by a cloud service
5050

51-
Coder includes [production-ready templates](../examples) for use with AWS EC2,
51+
Coder includes [production-ready templates](../examples/templates) for use with AWS EC2,
5252
Azure, Google Cloud, Kubernetes, and more.
5353

5454
## What Coder is *not*

docs/install.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,13 @@ Coder](https://github.com/coder/coder/releases) installed.
5252
coder templates init
5353
```
5454

55+
Choose the "Develop in Docker" example to generate a sample template in the
56+
`docker` subdirectory.
57+
5558
1. Navigate into the new directory and create a new template:
5659

5760
```console
58-
cd examples/docker
61+
cd docker
5962
coder templates create
6063
```
6164

docs/templates.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ to everybody. Users can also manually update their workspaces.
99

1010
## Manage templates
1111

12-
Coder provides production-ready [sample templates](../examples/), but you can
12+
Coder provides production-ready [sample templates](../examples/templates/), but you can
1313
modify the templates with Terraform.
1414

1515
```sh

docs/workspaces.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ resources](./templates.md#persistent-and-ephemeral-resources).
5858

5959
> ⚠️ To avoid data loss, refer to your template documentation for information on
6060
> where to store files, install software, etc., so that they persist. Default
61-
> templates are documented in [../examples](../examples/).
61+
> templates are documented in [../examples/templates](../examples/templates/).
6262
>
6363
> You can use `coder show <workspace-name>` to see which resources are
6464
> persistent and which are ephemeral.

examples/examples.go

Lines changed: 54 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"archive/tar"
55
"bytes"
66
"embed"
7+
"io"
8+
"io/fs"
79
"path"
810
"sync"
911

@@ -13,8 +15,7 @@ import (
1315
)
1416

1517
var (
16-
//go:embed */*.md
17-
//go:embed */*.tf
18+
//go:embed templates
1819
files embed.FS
1920

2021
examples = make([]Example, 0)
@@ -29,20 +30,30 @@ type Example struct {
2930
Markdown string `json:"markdown"`
3031
}
3132

33+
const rootDir = "templates"
34+
3235
// List returns all embedded examples.
3336
func List() ([]Example, error) {
3437
var returnError error
3538
parseExamples.Do(func() {
36-
dirs, err := files.ReadDir(".")
39+
files, err := fs.Sub(files, rootDir)
40+
if err != nil {
41+
returnError = xerrors.Errorf("get example fs: %w", err)
42+
}
43+
44+
dirs, err := fs.ReadDir(files, ".")
3745
if err != nil {
3846
returnError = xerrors.Errorf("read dir: %w", err)
3947
return
4048
}
4149

4250
for _, dir := range dirs {
51+
if !dir.IsDir() {
52+
continue
53+
}
4354
exampleID := dir.Name()
4455
// Each one of these is a example!
45-
readme, err := files.ReadFile(path.Join(dir.Name(), "README.md"))
56+
readme, err := fs.ReadFile(files, path.Join(dir.Name(), "README.md"))
4657
if err != nil {
4758
returnError = xerrors.Errorf("example %q does not contain README.md", exampleID)
4859
return
@@ -110,54 +121,65 @@ func Archive(exampleID string) ([]byte, error) {
110121
return nil, xerrors.Errorf("example with id %q not found", exampleID)
111122
}
112123

113-
entries, err := files.ReadDir(exampleID)
124+
exampleFiles, err := fs.Sub(files, path.Join(rootDir, exampleID))
114125
if err != nil {
115-
return nil, xerrors.Errorf("read dir: %w", err)
126+
return nil, xerrors.Errorf("get example fs: %w", err)
116127
}
117128

118129
var buffer bytes.Buffer
119130
tarWriter := tar.NewWriter(&buffer)
120131

121-
for _, entry := range entries {
122-
file, err := files.Open(path.Join(exampleID, entry.Name()))
123-
if err != nil {
124-
return nil, xerrors.Errorf("open file: %w", err)
125-
}
126-
127-
info, err := file.Stat()
132+
err = fs.WalkDir(exampleFiles, ".", func(path string, entry fs.DirEntry, err error) error {
128133
if err != nil {
129-
return nil, xerrors.Errorf("stat file: %w", err)
134+
return err
130135
}
131136

132-
if info.IsDir() {
133-
continue
134-
}
135-
136-
data := make([]byte, info.Size())
137-
_, err = file.Read(data)
137+
info, err := entry.Info()
138138
if err != nil {
139-
return nil, xerrors.Errorf("read data: %w", err)
139+
return xerrors.Errorf("stat file: %w", err)
140140
}
141141

142142
header, err := tar.FileInfoHeader(info, entry.Name())
143143
if err != nil {
144-
return nil, xerrors.Errorf("get file header: %w", err)
144+
return xerrors.Errorf("get file header: %w", err)
145145
}
146146
header.Mode = 0644
147147

148-
err = tarWriter.WriteHeader(header)
149-
if err != nil {
150-
return nil, xerrors.Errorf("write file: %w", err)
151-
}
152-
153-
_, err = tarWriter.Write(data)
154-
if err != nil {
155-
return nil, xerrors.Errorf("write: %w", err)
148+
if entry.IsDir() {
149+
header.Name = path + "/"
150+
151+
err = tarWriter.WriteHeader(header)
152+
if err != nil {
153+
return xerrors.Errorf("write file: %w", err)
154+
}
155+
} else {
156+
header.Name = path
157+
158+
file, err := exampleFiles.Open(path)
159+
if err != nil {
160+
return xerrors.Errorf("open file %s: %w", path, err)
161+
}
162+
defer file.Close()
163+
164+
err = tarWriter.WriteHeader(header)
165+
if err != nil {
166+
return xerrors.Errorf("write file: %w", err)
167+
}
168+
169+
_, err = io.Copy(tarWriter, file)
170+
if err != nil {
171+
return xerrors.Errorf("write: %w", err)
172+
}
156173
}
174+
return nil
175+
})
176+
if err != nil {
177+
return nil, xerrors.Errorf("walk example directory: %w", err)
157178
}
158-
err = tarWriter.Flush()
179+
180+
err = tarWriter.Close()
159181
if err != nil {
160-
return nil, xerrors.Errorf("flush archive: %w", err)
182+
return nil, xerrors.Errorf("close archive: %w", err)
161183
}
162184

163185
return buffer.Bytes(), nil

examples/examples_test.go

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

33
import (
4+
"archive/tar"
5+
"bytes"
6+
"errors"
7+
"io"
48
"testing"
59

610
"github.com/stretchr/testify/require"
@@ -17,3 +21,24 @@ func TestTemplate(t *testing.T) {
1721
_, err = examples.Archive(list[0].ID)
1822
require.NoError(t, err)
1923
}
24+
25+
func TestSubdirs(t *testing.T) {
26+
t.Parallel()
27+
tarData, err := examples.Archive("docker-image-builds")
28+
require.NoError(t, err)
29+
30+
tarReader := tar.NewReader(bytes.NewReader(tarData))
31+
entryPaths := make(map[byte][]string)
32+
for {
33+
header, err := tarReader.Next()
34+
if errors.Is(err, io.EOF) {
35+
break
36+
}
37+
require.NoError(t, err)
38+
39+
entryPaths[header.Typeflag] = append(entryPaths[header.Typeflag], header.Name)
40+
}
41+
42+
require.Subset(t, entryPaths[tar.TypeDir], []string{"./", "images/"})
43+
require.Subset(t, entryPaths[tar.TypeReg], []string{"README.md", "main.tf", "images/base.Dockerfile"})
44+
}

examples/README.md renamed to examples/templates/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ Clone this repository to create a project from any example listed here:
1010

1111
```sh
1212
git clone https://github.com/coder/coder
13-
cd examples/aws-macos
13+
cd examples/templates/aws-macos
1414
coder templates create
1515
```
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)