Skip to content

Commit 0c29907

Browse files
authored
Merge pull request #48 from stuartleeks/sl/substition
Add placeholder substitution to templates
2 parents 38be661 + 958592a commit 0c29907

File tree

11 files changed

+610
-76
lines changed

11 files changed

+610
-76
lines changed

.devcontainer/devcontainer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
// "forwardPorts": [],
5959

6060
// Use 'postCreateCommand' to run commands after the container is created.
61-
"postCreateCommand": "sudo chown $(whoami) /go/pkg",
61+
"postCreateCommand": "make post-create",
6262

6363
// Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root.
6464
"remoteUser": "vscode"

.vscode/launch.json

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,14 @@
1111
"mode": "auto",
1212
"program": "${workspaceFolder}/cmd/devcontainer",
1313
"env": {},
14-
"args": ["template", "list"]
14+
"args": ["template", "list"],
15+
"dlvLoadConfig": {
16+
"followPointers": true,
17+
"maxVariableRecurse": 1,
18+
"maxStringLen": 512,
19+
"maxArrayValues": 64,
20+
"maxStructFields": -1
21+
}
1522
},
1623
{
1724
"name": "Connect to server",
@@ -21,6 +28,13 @@
2128
"remotePath": "${workspaceFolder}",
2229
"port": 2345,
2330
"host": "127.0.0.1",
31+
"dlvLoadConfig": {
32+
"followPointers": true,
33+
"maxVariableRecurse": 1,
34+
"maxStringLen": 512,
35+
"maxArrayValues": 64,
36+
"maxStructFields": -1
37+
}
2438
},
2539
]
2640
}

.vscode/settings.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"go.delveConfig": {
3+
"dlvLoadConfig": {
4+
"followPointers": true,
5+
"maxVariableRecurse": 1,
6+
"maxStringLen": 512,
7+
"maxArrayValues": 64,
8+
"maxStructFields": -1
9+
},
10+
"apiVersion": 2,
11+
"showGlobalVariables": false,
12+
"debugAdapter": "legacy"
13+
}
14+
}

Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,9 @@ test:
3535

3636

3737
fmt:
38-
find . -name '*.go' | grep -v vendor | xargs gofmt -s -w
38+
find . -name '*.go' | grep -v vendor | xargs gofmt -s -w
39+
40+
41+
post-create:
42+
sudo chown vscode /go/pkg
43+
go get -u github.com/kyoh86/richgo

docs/snippet.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,13 @@ For example:
154154
]
155155
}
156156
```
157+
158+
### Placeholder Values
159+
160+
After content has been merged/added to files when performing snippet actions, the following placeholder values are substituted:
161+
162+
| Placeholder | Value |
163+
|------------------------------|----------------------------------------------------------------------------------------------------------------------|
164+
| `__DEVCONTAINER_NAME__` | The name of the dev container (from the `name` property in `devcontainer.json`) |
165+
| `__DEVCONTAINER_USER_NAME__` | The name of the user for dev container (from the `remoteuser` property in `devcontainer.json`, or `root` if not set) |
166+
| `__DEVCONTAINER_HOME__` | The home folder for the dev container (e.g. `/home/vscode` or `/root`) |

docs/template.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,20 @@ Assuming you cloned [github.com/microsoft/vscode-dev-containers/](https://github
8282
}
8383
```
8484

85+
## Placeholder Values
86+
87+
After content has been copied to the project folder from a template, the following placeholder values are substituted:
88+
89+
| Placeholder | Value |
90+
|------------------------------|----------------------------------------------------------------------------------------------------------------------|
91+
| `__DEVCONTAINER_NAME__` | The name of the dev container (from the `name` property in `devcontainer.json`) |
92+
| `__DEVCONTAINER_USER_NAME__` | The name of the user for dev container (from the `remoteuser` property in `devcontainer.json`, or `root` if not set) |
93+
| `__DEVCONTAINER_HOME__` | The home folder for the dev container (e.g. `/home/vscode` or `/root`) |
94+
8595
## Repository containers
8696

8797
VS Code dev containers have another feature called "Repository containers". These are a set of dev container definitions that VS Code will automatically apply to a project based on its git repo.
8898

8999
The default definitions are in the [microsoft/vscode-dev-containers](https://github.com/microsoft/vscode-dev-containers/tree/master/repository-containers) repo. If you look at the repo, you will see a `github.com` folder followed by paths for `<org>/<repo>`, e.g. `django/django`. The `https://github.com/django/django` repo doesn't contain a dev container definition, but VS Code will use the repository container definition from the `microsoft/vscode-dev-containers` repo.
90100

91-
You can also configure VS Code to look for additional local paths for repository containers by providing a value for the VS Code `remote.containers.repository-container-paths` setting (see [this issue](https://github.com/microsoft/vscode-remote-release/issues/3218) for more details).
92-
101+
You can also configure VS Code to look for additional local paths for repository containers by providing a value for the VS Code `remote.containers.repository-container-paths` setting (see [this issue](https://github.com/microsoft/vscode-remote-release/issues/3218) for more details).

internal/pkg/devcontainers/snippet.go

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package devcontainers
22

33
import (
4-
"bytes"
54
"encoding/json"
65
"fmt"
76
"io/ioutil"
@@ -225,7 +224,7 @@ func addFolderSnippetToDevContainer(projectFolder string, snippet *DevcontainerS
225224
return fmt.Errorf("content must be set for %s actions", action.Type)
226225
}
227226
dockerfileFilename := filepath.Join(projectFolder, ".devcontainer", "Dockerfile")
228-
err = insertDockerfileSnippet(dockerfileFilename, action.Content+"\n")
227+
err = insertDockerfileSnippet(projectFolder, dockerfileFilename, action.Content+"\n")
229228
if err != nil {
230229
return err
231230
}
@@ -251,11 +250,11 @@ RUN /tmp/%[2]s
251250
`, snippet.Name, scriptFilename)
252251
dockerfileFilename := filepath.Join(projectFolder, ".devcontainer", "Dockerfile")
253252

254-
err := insertDockerfileSnippet(dockerfileFilename, snippetContent)
253+
err := insertDockerfileSnippet(projectFolder, dockerfileFilename, snippetContent)
255254
return err
256255
}
257256

258-
func insertDockerfileSnippet(dockerfileFilename string, snippetContent string) error {
257+
func insertDockerfileSnippet(projectFolder string, dockerfileFilename string, snippetContent string) error {
259258

260259
buf, err := ioutil.ReadFile(dockerfileFilename)
261260
if err != nil {
@@ -266,7 +265,7 @@ func insertDockerfileSnippet(dockerfileFilename string, snippetContent string) e
266265
dockerFileLines := strings.Split(dockerfileContent, "\n")
267266
addSeparator := false
268267
addedSnippetContent := false
269-
var newContent bytes.Buffer
268+
var newContent strings.Builder
270269
for _, line := range dockerFileLines {
271270
if addSeparator {
272271
if _, err = newContent.WriteString("\n"); err != nil {
@@ -301,7 +300,14 @@ func insertDockerfileSnippet(dockerfileFilename string, snippetContent string) e
301300
}
302301
}
303302

304-
err = ioutil.WriteFile(dockerfileFilename, newContent.Bytes(), 0)
303+
content := newContent.String()
304+
values, err := getSubstitutionValuesFromFile(filepath.Join(projectFolder, ".devcontainer/devcontainer.json"))
305+
if err != nil {
306+
return fmt.Errorf("failed to get dev container values: %s", err)
307+
}
308+
content = performSubstitutionString(values, content)
309+
310+
err = ioutil.WriteFile(dockerfileFilename, []byte(content), 0)
305311

306312
return err
307313

@@ -333,21 +339,11 @@ func mergeJSON(projectFolder string, snippet *DevcontainerSnippet, relativeMerge
333339
return err
334340
}
335341

336-
// replace __DEVCONTAINER_NAME__ with name
337-
devcontainerName, devcontainerUserName := getDevcontainerNameAndUserName(filepath.Join(projectFolder, ".devcontainer/devcontainer.json"))
338-
if devcontainerName == "" {
339-
return fmt.Errorf("failed to get dev container name")
340-
}
341-
resultJSON = strings.ReplaceAll(resultJSON, "__DEVCONTAINER_NAME__", devcontainerName)
342-
if devcontainerUserName == "" {
343-
devcontainerUserName = "root"
344-
}
345-
devcontainerHome := "/home/" + devcontainerUserName
346-
if devcontainerUserName == "root" {
347-
devcontainerHome = "/root"
342+
values, err := getSubstitutionValuesFromFile(filepath.Join(projectFolder, ".devcontainer/devcontainer.json"))
343+
if err != nil {
344+
return fmt.Errorf("failed to get dev container values: %s", err)
348345
}
349-
resultJSON = strings.ReplaceAll(resultJSON, "__DEVCONTAINER_USER_NAME__", devcontainerUserName)
350-
resultJSON = strings.ReplaceAll(resultJSON, "__DEVCONTAINER_HOME__", devcontainerHome)
346+
resultJSON = performSubstitutionString(values, resultJSON)
351347

352348
err = ioutil.WriteFile(basePath, []byte(resultJSON), 0666)
353349
if err != nil {
@@ -372,17 +368,23 @@ func loadJSONDocument(path string) (*dora_ast.RootNode, error) {
372368
return &baseDocument, nil
373369
}
374370

375-
func getDevcontainerNameAndUserName(devContainerJsonPath string) (string, string) {
371+
type SubstitutionValues struct {
372+
Name string
373+
UserName string
374+
HomeFolder string
375+
}
376+
377+
func getSubstitutionValuesFromFile(devContainerJsonPath string) (*SubstitutionValues, error) {
376378
// This doesn't use standard `json` pkg as devcontainer.json permits comments (and the default templates include them!)
377379

378380
buf, err := ioutil.ReadFile(devContainerJsonPath)
379381
if err != nil {
380-
return "", ""
382+
return nil, err
381383
}
382384

383385
c, err := dora.NewFromBytes(buf)
384386
if err != nil {
385-
return "", ""
387+
return nil, err
386388
}
387389

388390
name, err := c.GetString("$.name")
@@ -391,8 +393,35 @@ func getDevcontainerNameAndUserName(devContainerJsonPath string) (string, string
391393
}
392394
userName, err := c.GetString("$.remoteUser")
393395
if err != nil {
394-
userName = ""
396+
userName = "root"
395397
}
398+
homeFolder := "/home/" + userName
399+
if userName == "root" {
400+
homeFolder = "/root"
401+
}
402+
403+
return &SubstitutionValues{
404+
Name: name,
405+
UserName: userName,
406+
HomeFolder: homeFolder,
407+
}, nil
408+
}
409+
410+
func performSubstitutionFile(substitutionValues *SubstitutionValues, filename string) error {
411+
buf, err := ioutil.ReadFile(filename)
412+
if err != nil {
413+
return err
414+
}
415+
content := string(buf)
416+
content = performSubstitutionString(substitutionValues, content)
417+
err = ioutil.WriteFile(filename, []byte(content), 0)
418+
return err
419+
}
396420

397-
return name, userName
421+
func performSubstitutionString(substitutionValues *SubstitutionValues, content string) string {
422+
// replace __DEVCONTAINER_NAME__ with name etc
423+
content = strings.ReplaceAll(content, "__DEVCONTAINER_NAME__", substitutionValues.Name)
424+
content = strings.ReplaceAll(content, "__DEVCONTAINER_USER_NAME__", substitutionValues.UserName)
425+
content = strings.ReplaceAll(content, "__DEVCONTAINER_HOME__", substitutionValues.HomeFolder)
426+
return content
398427
}

0 commit comments

Comments
 (0)