Skip to content

Commit

Permalink
Refactor langage/toolchain abstractions
Browse files Browse the repository at this point in the history
  • Loading branch information
phamann committed Oct 26, 2020
1 parent 1898cc3 commit 5d59eb8
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 84 deletions.
2 changes: 1 addition & 1 deletion pkg/app/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func Run(args []string, env config.Environment, file config.File, configFilePath
serviceVersionLock := serviceversion.NewLockCommand(serviceVersionRoot.CmdClause, &globals)

computeRoot := compute.NewRootCommand(app, &globals)
computeInit := compute.NewInitCommand(computeRoot.CmdClause, &globals)
computeInit := compute.NewInitCommand(computeRoot.CmdClause, httpClient, &globals)
computeBuild := compute.NewBuildCommand(computeRoot.CmdClause, httpClient, &globals)
computeDeploy := compute.NewDeployCommand(computeRoot.CmdClause, httpClient, &globals)
computeUpdate := compute.NewUpdateCommand(computeRoot.CmdClause, httpClient, &globals)
Expand Down
38 changes: 7 additions & 31 deletions pkg/compute/assemblyscript.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,33 +16,9 @@ import (
// AssemblyScript implements Toolchain for the AssemblyScript language.
type AssemblyScript struct{}

// Name implements the Toolchain interface and returns the name of the toolchain.
func (a AssemblyScript) Name() string { return "assemblyscript" }

// DisplayName implements the Toolchain interface and returns the name of the
// toolchain suitable for displaying or printing to output.
func (a AssemblyScript) DisplayName() string { return "AssemblyScript (beta)" }

// StarterKits implements the Toolchain interface and returns the list of
// starter kits that can be used to initialize a new package for the toolchain.
func (a AssemblyScript) StarterKits() []StarterKit {
return []StarterKit{
{
Name: "Default",
Path: "https://github.com/fastly/compute-starter-kit-assemblyscript-default",
Tag: "v0.1.0",
},
}
}

// SourceDirectory implements the Toolchain interface and returns the source
// directory for AssemblyScript packages.
func (a AssemblyScript) SourceDirectory() string { return "src" }

// IncludeFiles implements the Toolchain interface and returns a list of
// additional files to include in the package archive for AssemblyScript packages.
func (a AssemblyScript) IncludeFiles() []string {
return []string{"package.json"}
// NewAssemblyScript constructs a new AssemblyScript.
func NewAssemblyScript() *AssemblyScript {
return &AssemblyScript{}
}

// Verify implements the Toolchain interface and verifies whether the
Expand Down Expand Up @@ -170,16 +146,16 @@ func (a AssemblyScript) Build(out io.Writer, verbose bool) error {
// Check if bin directory exists and create if not.
pwd, err := os.Getwd()
if err != nil {
return fmt.Errorf("error getting current working directory: %w", err)
return fmt.Errorf("getting current working directory: %w", err)
}
binDir := filepath.Join(pwd, "bin")
if err := common.MakeDirectoryIfNotExists(binDir); err != nil {
return fmt.Errorf("error making bin directory: %w", err)
return fmt.Errorf("making bin directory: %w", err)
}

npmdir, err := getNpmBinPath()
if err != nil {
return err
return fmt.Errorf("getting npm path: %w", err)
}

args := []string{
Expand Down Expand Up @@ -207,7 +183,7 @@ func (a AssemblyScript) Build(out io.Writer, verbose bool) error {
func getNpmBinPath() (string, error) {
path, err := exec.Command("npm", "bin").Output()
if err != nil {
return "", fmt.Errorf("error getting npm bin path: %w", err)
return "", err
}
return strings.TrimSpace(string(path)), nil
}
Expand Down
62 changes: 50 additions & 12 deletions pkg/compute/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,44 @@ const IgnoreFilePath = ".fastlyignore"

// Toolchain abstracts a Compute@Edge source language toolchain.
type Toolchain interface {
Name() string
DisplayName() string
StarterKits() []StarterKit
SourceDirectory() string
IncludeFiles() []string
Initialize(out io.Writer) error
Verify(out io.Writer) error
Build(out io.Writer, verbose bool) error
}

// Language models a Compute@Edge source language.
type Language struct {
Name string
DisplayName string
StarterKits []StarterKit
SourceDirectory string
IncludeFiles []string

Toolchain
}

// LanguageOptions models configuration options for a Language.
type LanguageOptions struct {
Name string
DisplayName string
StarterKits []StarterKit
SourceDirectory string
IncludeFiles []string
Toolchain Toolchain
}

// NewLanguage constructs a new Language from a LangaugeOptions.
func NewLanguage(options *LanguageOptions) *Language {
return &Language{
options.Name,
options.DisplayName,
options.StarterKits,
options.SourceDirectory,
options.IncludeFiles,
options.Toolchain,
}
}

// BuildCommand produces a deployable artifact from files on the local disk.
type BuildCommand struct {
common.Base
Expand Down Expand Up @@ -103,28 +131,38 @@ func (c *BuildCommand) Exec(in io.Reader, out io.Writer) (err error) {
}
name = sanitize.BaseName(name)

var toolchain Toolchain
var language *Language
switch lang {
case "assemblyscript":
toolchain = &AssemblyScript{}
language = NewLanguage(&LanguageOptions{
Name: "assemblyscript",
SourceDirectory: "src",
IncludeFiles: []string{"package.json"},
Toolchain: NewAssemblyScript(),
})
case "rust":
toolchain = &Rust{c.client}
language = NewLanguage(&LanguageOptions{
Name: "rust",
SourceDirectory: "src",
IncludeFiles: []string{"Cargo.toml"},
Toolchain: NewRust(c.client),
})
default:
return fmt.Errorf("unsupported language %s", lang)
}

if !c.force {
progress.Step(fmt.Sprintf("Verifying local %s toolchain...", lang))

err = toolchain.Verify(progress)
err = language.Verify(progress)
if err != nil {
return err
}
}

progress.Step(fmt.Sprintf("Building package using %s toolchain...", lang))

if err := toolchain.Build(progress, c.Globals.Flag.Verbose); err != nil {
if err := language.Build(progress, c.Globals.Flag.Verbose); err != nil {
return err
}

Expand All @@ -135,7 +173,7 @@ func (c *BuildCommand) Exec(in io.Reader, out io.Writer) (err error) {
files := []string{
ManifestFilename,
}
files = append(files, toolchain.IncludeFiles()...)
files = append(files, language.IncludeFiles...)

ignoreFiles, err := getIgnoredFiles(IgnoreFilePath)
if err != nil {
Expand All @@ -149,7 +187,7 @@ func (c *BuildCommand) Exec(in io.Reader, out io.Writer) (err error) {
files = append(files, binFiles...)

if c.includeSrc {
srcFiles, err := getNonIgnoredFiles(toolchain.SourceDirectory(), ignoreFiles)
srcFiles, err := getNonIgnoredFiles(language.SourceDirectory, ignoreFiles)
if err != nil {
return err
}
Expand Down
78 changes: 55 additions & 23 deletions pkg/compute/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"time"

"github.com/dustinkirkland/golang-petname"
"github.com/fastly/cli/pkg/api"
"github.com/fastly/cli/pkg/common"
"github.com/fastly/cli/pkg/compute/manifest"
"github.com/fastly/cli/pkg/config"
Expand All @@ -34,9 +35,21 @@ var (
domainNameRegEx = regexp.MustCompile(`(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]`)
fastlyOrgRegEx = regexp.MustCompile(`^https:\/\/github\.com\/fastly`)
fastlyFileIgnoreListRegEx = regexp.MustCompile(`\.github|LICENSE|SECURITY\.md|CHANGELOG\.md|screenshot\.png`)
languages = []Toolchain{
&Rust{},
&AssemblyScript{},
starterKits = map[string][]StarterKit{
"assemblyscript": {
{
Name: "Default",
Path: "https://github.com/fastly/compute-starter-kit-assemblyscript-default",
Tag: "v0.1.0",
},
},
"rust": {
{
Name: "Default",
Path: "https://github.com/fastly/fastly-template-rust-default.git",
Branch: "0.4.0",
},
},
}
)

Expand All @@ -51,6 +64,7 @@ type StarterKit struct {
// InitCommand initializes a Compute@Edge project package on the local machine.
type InitCommand struct {
common.Base
client api.HTTPClient
manifest manifest.Data
language string
from string
Expand All @@ -62,9 +76,10 @@ type InitCommand struct {
}

// NewInitCommand returns a usable command registered under the parent.
func NewInitCommand(parent common.Registerer, globals *config.Data) *InitCommand {
func NewInitCommand(parent common.Registerer, client api.HTTPClient, globals *config.Data) *InitCommand {
var c InitCommand
c.Globals = globals
c.client = client
c.manifest.File.Read(manifest.Filename)
c.CmdClause = parent.Command("init", "Initialize a new Compute@Edge package locally")
c.CmdClause.Flag("service-id", "Existing service ID to use. By default, this command creates a new service").Short('s').StringVar(&c.manifest.Flag.ServiceID)
Expand Down Expand Up @@ -119,9 +134,24 @@ func (c *InitCommand) Exec(in io.Reader, out io.Writer) (err error) {
name string
description string
authors []string
language Toolchain
language *Language
)

languages := []*Language{
NewLanguage(&LanguageOptions{
Name: "rust",
DisplayName: "Rust",
StarterKits: starterKits["rust"],
Toolchain: NewRust(c.client),
}),
NewLanguage(&LanguageOptions{
Name: "assemblyscript",
DisplayName: "AssemblyScript (beta)",
StarterKits: starterKits["assemblyscript"],
Toolchain: NewAssemblyScript(),
}),
}

name, _ = c.manifest.Name()
description, _ = c.manifest.Description()
authors, _ = c.manifest.Authors()
Expand Down Expand Up @@ -194,9 +224,9 @@ func (c *InitCommand) Exec(in io.Reader, out io.Writer) (err error) {
if c.language == "" {
text.Output(out, "%s", text.Bold("Language:"))
for i, lang := range languages {
text.Output(out, "[%d] %s", i+1, lang.DisplayName())
text.Output(out, "[%d] %s", i+1, lang.DisplayName)
}
option, err := text.Input(out, "Choose option: [1] ", in, validateLanguageOption)
option, err := text.Input(out, "Choose option: [1] ", in, validateLanguageOption(languages))
if err != nil {
return fmt.Errorf("reading input %w", err)
}
Expand All @@ -210,18 +240,18 @@ func (c *InitCommand) Exec(in io.Reader, out io.Writer) (err error) {
}
} else {
for _, l := range languages {
if strings.EqualFold(c.language, l.Name()) {
if strings.EqualFold(c.language, l.Name) {
language = l
}
}
}

if c.from == "" && !c.manifest.File.Exists() {
text.Output(out, "%s", text.Bold("Starter kit:"))
for i, kit := range language.StarterKits() {
for i, kit := range language.StarterKits {
text.Output(out, "[%d] %s (%s)", i+1, kit.Name, kit.Path)
}
option, err := text.Input(out, "Choose option or type URL: [1] ", in, validateTemplateOptionOrURL(language.StarterKits()))
option, err := text.Input(out, "Choose option or type URL: [1] ", in, validateTemplateOptionOrURL(language.StarterKits))
if err != nil {
return fmt.Errorf("error reading input %w", err)
}
Expand All @@ -230,7 +260,7 @@ func (c *InitCommand) Exec(in io.Reader, out io.Writer) (err error) {
}

if i, err := strconv.Atoi(option); err == nil {
template := language.StarterKits()[i-1]
template := language.StarterKits[i-1]
c.from = template.Path
c.branch = template.Branch
c.tag = template.Tag
Expand Down Expand Up @@ -434,8 +464,8 @@ func (c *InitCommand) Exec(in io.Reader, out io.Writer) (err error) {
fmt.Fprintf(progress, "Setting version in manifest to %d...\n", version)
m.Version = version

fmt.Fprintf(progress, "Setting language in manifest to %s...\n", language.Name())
m.Language = language.Name()
fmt.Fprintf(progress, "Setting language in manifest to %s...\n", language.Name)
m.Language = language.Name

if err := m.Write(filepath.Join(c.path, ManifestFilename)); err != nil {
return fmt.Errorf("error saving package manifest: %w", err)
Expand Down Expand Up @@ -522,18 +552,20 @@ func tempDir(prefix string) (abspath string, err error) {
return abspath, nil
}

func validateLanguageOption(input string) error {
errMsg := fmt.Errorf("must be a valid option")
if input == "" {
return nil
}
if option, err := strconv.Atoi(input); err == nil {
if option > len(languages) {
return errMsg
func validateLanguageOption(languages []*Language) func(string) error {
return func(input string) error {
errMsg := fmt.Errorf("must be a valid option")
if input == "" {
return nil
}
return nil
if option, err := strconv.Atoi(input); err == nil {
if option > len(languages) {
return errMsg
}
return nil
}
return errMsg
}
return errMsg
}

func validateTemplateOptionOrURL(templates []StarterKit) func(string) error {
Expand Down
20 changes: 3 additions & 17 deletions pkg/compute/rust.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,23 +81,9 @@ type Rust struct {
client api.HTTPClient
}

// Name implements the Toolchain interface and returns the name of the toolchain.
func (r Rust) Name() string { return "rust" }

// DisplayName implements the Toolchain interface and returns the name of the
// toolchain suitable for displaying or printing to output.
func (r Rust) DisplayName() string { return "Rust" }

// StarterKits implements the Toolchain interface and returns the list of
// starter kits that can be used to initialize a new package for the toolchain.
func (r Rust) StarterKits() []StarterKit {
return []StarterKit{
{
Name: "Default",
Path: "https://github.com/fastly/fastly-template-rust-default.git",
Branch: "0.4.0",
},
}
// NewRust constructs a new Rust.
func NewRust(client api.HTTPClient) *Rust {
return &Rust{client}
}

// SourceDirectory implements the Toolchain interface and returns the source
Expand Down

0 comments on commit 5d59eb8

Please sign in to comment.