Skip to content

Commit ffe6275

Browse files
authored
feat: creating application from template (#8)
* feat: helper file and directory structs * wip: initialize application from templates * chore: cleanup * refactor: use file struct * refactor: config exists * wip: config command * chore: update error message * feat: generate templates * chore: adding package for handlebars * chore: update ignore list * feat: update dependency config and init * feat: depedency config and command init * feat: prompts building * chore: linting * feat: configuration of applate * feat: install of dependencies * fix: linting * fix: git cloning template if does not exist * feat: adding language version to vars * feat: adding app init to readme
1 parent c220983 commit ffe6275

22 files changed

Lines changed: 671 additions & 94 deletions

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,8 @@ info.md
1818
# Dependency directories (remove the comment below to include it)
1919
# vendor/
2020
.stitch
21-
docker-compose.*
21+
docker-compose.*
22+
23+
# applate testing
24+
pkg
25+
applate

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Stitch is a utility CLI that streamlines the local development of micro-services
1212

1313
# Commands
1414

15-
| Command | Description |
16-
| -------------------- | ------------------------------------------ |
17-
| `stitch compose add` | adds a service to your docker-compose file |
15+
| Command | Description |
16+
| -------------------- | --------------------------------------------- |
17+
| `stitch compose add` | adds a service to your docker-compose file |
18+
| `stitch app init` | initializes an application template (applate) |

cmd/add.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import (
2121

2222
"github.com/openopsdev/go-cli-commons/logger"
2323
"github.com/openopsdev/go-cli-commons/prompts"
24-
"github.com/roger-king/stitch/configs"
25-
"github.com/roger-king/stitch/services"
24+
"github.com/openopsdev/stitch/configs"
25+
"github.com/openopsdev/stitch/services"
2626
"github.com/spf13/cobra"
2727
)
2828

cmd/app.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
Copyright © 2021 NAME HERE <EMAIL ADDRESS>
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package cmd
17+
18+
import (
19+
"github.com/spf13/cobra"
20+
)
21+
22+
// appCmd represents the app command
23+
var appCmd = &cobra.Command{
24+
Use: "app",
25+
Short: "create an application from template",
26+
Long: ``,
27+
}
28+
29+
func init() {
30+
rootCmd.AddCommand(appCmd)
31+
}

cmd/compose.go

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,6 @@ var composeCmd = &cobra.Command{
2424
Use: "compose",
2525
Short: "Sets up composition of your applications",
2626
Long: ``,
27-
// Run: func(cmd *cobra.Command, args []string) {
28-
// ps := prompts.Prompts {
29-
// "serviceName": &prompts.UserInput{
30-
// Label: "name of service",
31-
// },
32-
// "image": &prompts.UserInput{
33-
// Label: "image name[:tag]",
34-
// },
35-
// }
36-
// answers := ps.Run()
37-
// fmt.Print(answers)
38-
39-
// dc, _ := configs.NewDockerCompose()
40-
41-
// dc.Services[answers["serviceName"]] = &configs.Service{
42-
// Image: answers["image"],
43-
// }
44-
45-
// configs.Render("docker-compose.yml", dc)
46-
// },
4727
}
4828

4929
func init() {

cmd/config.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
Copyright © 2021 NAME HERE <EMAIL ADDRESS>
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package cmd
17+
18+
import (
19+
"os"
20+
"path"
21+
22+
"github.com/openopsdev/go-cli-commons/logger"
23+
"github.com/spf13/cobra"
24+
)
25+
26+
// configCmd represents the config command
27+
var configCmd = &cobra.Command{
28+
Use: "config",
29+
Short: "setup the global configuration for stitch",
30+
Long: ``,
31+
Run: func(cmd *cobra.Command, args []string) {
32+
home, _ := os.UserHomeDir()
33+
34+
err := os.MkdirAll(path.Join(home, ".stitch"), os.ModePerm)
35+
36+
if err != nil {
37+
logger.Fatal(err.Error())
38+
}
39+
},
40+
}
41+
42+
func init() {
43+
rootCmd.AddCommand(configCmd)
44+
}

cmd/init.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919
"fmt"
2020

2121
"github.com/openopsdev/go-cli-commons/logger"
22-
"github.com/roger-king/stitch/configs"
22+
"github.com/openopsdev/stitch/configs"
2323
"github.com/spf13/cobra"
2424
)
2525

@@ -28,6 +28,7 @@ var initCmd = &cobra.Command{
2828
Use: "init",
2929
Short: "Interactively create or update a .stitch/config.yaml file",
3030
Run: func(cmd *cobra.Command, args []string) {
31+
// globalConfig, hasExisting := configs.NewGlobalStitchConfig()
3132
config, hasExisting := configs.NewStichConfig()
3233
if !hasExisting {
3334
config.Docker = &configs.DockerConfig{
@@ -39,7 +40,7 @@ var initCmd = &cobra.Command{
3940
err := configs.Render("./.stitch/config.yaml", config)
4041

4142
if err != nil {
42-
logger.Fatal(fmt.Errorf("Found an error rendering %v", err).Error())
43+
logger.Fatal(fmt.Errorf("found an error rendering %v", err).Error())
4344
}
4445
},
4546
}

cmd/initApp.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
Copyright © 2021 NAME HERE <EMAIL ADDRESS>
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package cmd
17+
18+
import (
19+
"github.com/openopsdev/go-cli-commons/logger"
20+
"github.com/openopsdev/stitch/configs"
21+
"github.com/spf13/cobra"
22+
)
23+
24+
// initAppCmd represents the initApp command
25+
var initAppCmd = &cobra.Command{
26+
Use: "init",
27+
Short: "A brief description of your command",
28+
Long: ``,
29+
Run: func(cmd *cobra.Command, args []string) {
30+
var answers map[string]string
31+
a := args[0]
32+
appplate := configs.NewApplate(a, answers)
33+
applateErrors := appplate.Run()
34+
35+
if len(applateErrors) > 0 {
36+
for _, e := range applateErrors {
37+
logger.Error(e.Error())
38+
}
39+
return
40+
}
41+
},
42+
}
43+
44+
func init() {
45+
appCmd.AddCommand(initAppCmd)
46+
}

configs/applate.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package configs
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"os"
7+
"path"
8+
"path/filepath"
9+
"strings"
10+
11+
"github.com/openopsdev/go-cli-commons/logger"
12+
"github.com/openopsdev/stitch/services"
13+
"github.com/openopsdev/stitch/templates"
14+
"github.com/openopsdev/stitch/utils"
15+
"gopkg.in/yaml.v2"
16+
)
17+
18+
type Templater interface {
19+
Run()
20+
}
21+
22+
type Applate struct {
23+
Source string
24+
Answer map[string]string
25+
Dependency DependencyConfig
26+
}
27+
28+
func findCacheDir() string {
29+
homedir, _ := os.UserHomeDir()
30+
return path.Join(homedir, ".stitch/applates")
31+
}
32+
33+
var cacheDir = findCacheDir()
34+
35+
func NewApplate(source string, answers map[string]string) Applate {
36+
var promptConfig PromptConfig
37+
var dependencyConfig DependencyConfig
38+
applateDir := path.Join(cacheDir, source)
39+
40+
if err := configExists(applateDir, ".stitch/applate.yaml"); os.IsNotExist(err) {
41+
err = services.GitClone(source, applateDir)
42+
43+
if err != nil {
44+
logger.Fatal(fmt.Errorf("failed to pull %s applate: %v", source, err.Error()).Error())
45+
}
46+
}
47+
48+
// TODO: handle no prompt config file
49+
applatePromptConfigPath := path.Join(applateDir, ".stitch/prompts.yaml")
50+
applatePromptContent, err := ioutil.ReadFile(applatePromptConfigPath)
51+
52+
if err != nil {
53+
logger.Fatal(fmt.Errorf("failed to read prompt config: %v", err).Error())
54+
}
55+
56+
err = yaml.Unmarshal(applatePromptContent, &promptConfig)
57+
58+
if err != nil {
59+
logger.Fatal(fmt.Errorf("failed to build prompt config: %v", err).Error())
60+
}
61+
prompts := promptConfig.Build()
62+
promptAnswers := prompts.Run()
63+
64+
// TODO: split into a Go Routine
65+
applateConfigFilePath := path.Join(applateDir, ".stitch/applate.yaml")
66+
applateConfigContents, err := ioutil.ReadFile(applateConfigFilePath)
67+
68+
if err != nil {
69+
logger.Fatal(fmt.Errorf("failed to read existing config: %v", err).Error())
70+
}
71+
72+
err = yaml.Unmarshal(applateConfigContents, &dependencyConfig)
73+
74+
if err != nil {
75+
logger.Fatal(fmt.Errorf("failed to build applate config: %v", err).Error())
76+
}
77+
78+
if len(dependencyConfig.Version) > 0 {
79+
// Adding the global language version to answers
80+
promptAnswers["version"] = dependencyConfig.Version
81+
}
82+
83+
dependencyConfig.Vars = promptAnswers
84+
85+
err = dependencyConfig.Init()
86+
87+
if err != nil {
88+
logger.Fatal(fmt.Errorf("failed to init project: %v", err).Error())
89+
}
90+
91+
return Applate{
92+
Source: applateDir,
93+
Dependency: dependencyConfig,
94+
Answer: promptAnswers,
95+
}
96+
}
97+
98+
func (a Applate) Run() []error {
99+
var runErrors []error
100+
files, err := a.findTemplates()
101+
102+
if err != nil {
103+
runErrors = append(runErrors, fmt.Errorf("cannot find tempalte: %v", err.Error()))
104+
return runErrors
105+
}
106+
107+
for _, f := range files {
108+
file := utils.File{
109+
Source: f,
110+
}
111+
rawData, err := file.Read()
112+
113+
if err != nil {
114+
runErrors = append(runErrors, fmt.Errorf("failed to read %s: %v", f, err.Error()))
115+
continue
116+
}
117+
118+
template := templates.HandlebarTemplate{
119+
Raw: rawData,
120+
Destination: a.buildDestinationPath(f),
121+
}
122+
123+
err = template.Save(a.Answer)
124+
125+
if err != nil {
126+
runErrors = append(runErrors, fmt.Errorf("failed to generate %s template: %v", f, err.Error()))
127+
}
128+
}
129+
130+
if a.Dependency.Dependencies != nil {
131+
err = a.Dependency.InstallDependencies()
132+
133+
if err != nil {
134+
runErrors = append(runErrors, fmt.Errorf("failed to install: %v", err.Error()))
135+
}
136+
}
137+
138+
return runErrors
139+
}
140+
141+
func (a Applate) findTemplates() ([]string, error) {
142+
var files []string
143+
144+
err := filepath.Walk(a.Source, func(path string, info os.FileInfo, err error) error {
145+
isConfig := strings.Contains(path, "applate.yaml")
146+
gitDir := strings.Contains(path, ".git")
147+
isFileToAdd := !isConfig && !info.IsDir() && !gitDir
148+
if isFileToAdd {
149+
files = append(files, path)
150+
}
151+
152+
return nil
153+
})
154+
155+
return files, err
156+
}
157+
158+
func (a Applate) buildDestinationPath(p string) string {
159+
s := strings.Split(p, a.Source)
160+
_, s = s[0], s[1:]
161+
updatedPath := strings.Join(s, "/")
162+
cwd, _ := os.Getwd()
163+
return path.Join(cwd, updatedPath)
164+
}

0 commit comments

Comments
 (0)