feat(scaffolder): actually run the cookie cutter now in the folder and pass through the metadata to the cookiecutter.json

This commit is contained in:
blam
2020-02-06 17:42:23 +01:00
parent b844de0cd5
commit 4697245252
2 changed files with 90 additions and 29 deletions
+37 -24
View File
@@ -7,10 +7,10 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"log"
"os/exec"
identity "github.com/spotify/backstage/backend/proto/identity/v1"
pb "github.com/spotify/backstage/backend/proto/scaffolder/v1"
"github.com/spotify/backstage/scaffolder/cutter"
"github.com/spotify/backstage/scaffolder/fs"
"github.com/spotify/backstage/scaffolder/remote"
"github.com/spotify/backstage/scaffolder/repository"
@@ -21,6 +21,7 @@ type Server struct {
repository *repository.Repository
github *remote.Github
fs *fs.Filesystem
cookie *cutter.Cutter
}
// NewServer creates a new server for with all the things
@@ -34,38 +35,49 @@ func NewServer() *Server {
func (s *Server) Create(ctx context.Context, req *pb.CreateRequest) (*pb.CreateReply, error) {
// first create the repository with github
log.Printf("Creating repository for Component %s", req.ComponentId)
// repo := remote.Repository{
// Name: req.ComponentId,
// Org: req.Org,
// Private: req.Private,
// }
// if _, err := s.github.CreateRepository(repo); err != nil {
// return nil, status.Error(codes.Internal, fmt.Sprintf("Could not create repository %s/%s", req.Org, req.ComponentId))
// }
marshaller := &jsonpb.Marshaler{}
d, e := marshaller.MarshalToString(req.Metadata)
repo := remote.Repository{
Name: req.ComponentId,
Org: req.Org,
Private: req.Private,
}
if _, err := s.github.CreateRepository(repo); err != nil {
return nil, status.Error(codes.Internal, fmt.Sprintf("Could not create repository %s/%s", req.Org, req.ComponentId))
}
// move the template into a temporary directory
tempFolder, err := s.fs.PrepareTemplate(
fs.Template{
ID: req.TemplateId,
Metadata: req.Metadata,
ID: req.TemplateId,
},
)
if err != nil {
return nil, status.Error(codes.Internal, fmt.Sprintf("Could not prepare the template"))
return nil, status.Error(codes.Internal, "Could not prepare the template")
}
log.Printf("Created temporary folder %s", tempFolder)
// TODO(blam): Probably need to wrap this in a timeout or something to avoid waiting to long
cmd := exec.Command("cookiecutter", "--no-input", "-v", tempFolder)
cmd.Dir = tempFolder
err := cmd.Run()
// get the optional metadatafields from the json
marshaler := &jsonpb.Marshaler{}
cutterMetadata, err := marshaler.MarshalToString(req.Metadata)
fmt.Println(output)
fmt.Println(err)
if err != nil {
return nil, status.Error(codes.Internal, "Could not marshal the cookiecutter metadata")
}
cookieTemplate := cutter.CookieCutterTemplate{
Path: tempFolder,
ComponentID: req.ComponentId,
Metadata: cutterMetadata,
}
// create the cookicutter json
if err := s.cookie.WriteMetadata(cookieTemplate); err != nil {
return nil, status.Error(codes.Internal, "Could not write cookie metadata")
}
// run the cookiecutter on the folder
if err := s.cookie.Run(cookieTemplate); err != nil {
return nil, status.Error(codes.Internal, "Failed to run the cookie cutter")
}
// use git bindings to add the remote with access token and push to the directory
return nil, nil
@@ -73,7 +85,7 @@ func (s *Server) Create(ctx context.Context, req *pb.CreateRequest) (*pb.CreateR
// ListTemplates returns the local templatess
func (s *Server) ListTemplates(ctx context.Context, req *pb.Empty) (*pb.ListTemplatesReply, error) {
// todo (blam): yes we currently read the disk on every load. but it's fine for now 🤷‍♂️
// TODO(blam): yes we currently read the disk on every load. but it's fine for now 🤷‍♂️
definitions, err := s.repository.Load()
var templates []*pb.Template
@@ -82,6 +94,7 @@ func (s *Server) ListTemplates(ctx context.Context, req *pb.Empty) (*pb.ListTemp
Id: definition.ID,
Name: definition.Name,
Description: definition.Description,
// need to actually call the idenity service here to get the
// actual user and propgate back when needed.
User: &identity.User{
+53 -5
View File
@@ -1,13 +1,61 @@
package cutter
import (
"encoding/json"
"fmt"
"log"
"os"
"os/exec"
)
// Cutter is the wrapper around cookiecutter
type Cutter interface {
type Cutter struct {
}
type Template {
// CookieCutterTemplate holds the template ID and metadata for copying
type CookieCutterTemplate struct {
Path string
Metadata string
ComponentID string
}
// Run runs cookiecutter in the folder provided
func (c *Cutter) Run() error {
// WriteMetadata runs cookiecutter in the folder provided
func (c *Cutter) WriteMetadata(template CookieCutterTemplate) error {
var temporaryCookieCutter map[string]interface{}
err := json.Unmarshal([]byte(template.Metadata), &temporaryCookieCutter)
if err != nil {
return err
}
temporaryCookieCutter["component_id"] = template.ComponentID
finalMetadata, _ := json.Marshal(temporaryCookieCutter)
file, err := os.Create(fmt.Sprintf("%s/cookiecutter.json", template.Path))
if err != nil {
return err
}
defer file.Close()
_, err = file.Write(finalMetadata)
return err
}
// Run will run the cookie cutter
func (c *Cutter) Run(template CookieCutterTemplate) error {
// TODO(blam): Probably need to wrap this in a timeout or something to avoid waiting to long
log.Printf("Running cookiecutter on %s", template.Path)
log.Printf("With metadata %s", template.Metadata)
cmd := exec.Command("cookiecutter", "--no-input", "-v", template.Path)
cmd.Dir = template.Path
output, err := cmd.Output()
if err != nil {
log.Printf("Cookie cutter failed with output: %s", output)
}
return err
}