Skip to content

Commit

Permalink
Extract config type
Browse files Browse the repository at this point in the history
  • Loading branch information
y-yagi committed Oct 1, 2020
1 parent d8f4674 commit 7391711
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 97 deletions.
114 changes: 114 additions & 0 deletions config.go
@@ -0,0 +1,114 @@
package main

import (
"bytes"
"fmt"
"html/template"
"runtime"

"github.com/BurntSushi/toml"
)

// Command represents a command to run.
type Command struct {
name string
args []string
envs []string
}

// BakeFileVariable represents a variables of configuration file.
type BakeFileVariable struct {
OS string
}

// Config represents a configuration file.
type Config struct {
file string
Tasks map[string]Task
}

// NewConfig creates a new Config.
func NewConfig(file string) *Config {
c := &Config{file: file, Tasks: map[string]Task{}}
return c
}

// Parse a config file.
func (c *Config) Parse() error {
t, err := template.ParseFiles(c.file)
if err != nil {
return err
}

configFile := new(bytes.Buffer)
tv := BakeFileVariable{OS: runtime.GOOS}
if err = t.Execute(configFile, tv); err != nil {
return err
}

var p toml.Primitive
md, err := toml.Decode(configFile.String(), &p)
if err != nil {
return err
}

if err := md.PrimitiveDecode(p, &c.Tasks); err != nil {
return err
}

return nil
}

// BuildCommands builds commands.
func (c *Config) BuildCommands(target string) ([]Command, error) {
task, found := c.Tasks[target]
if !found {
err := fmt.Errorf("'%s' is not defined", target)
return nil, err
}

dependencies := task.Dependencies
definedTasks := map[string]bool{}
visitedTasks := map[string]bool{}
commands := []Command{}

for len(dependencies) > 0 {
dependency := dependencies[0]

t, found := c.Tasks[dependency]
if !found {
err := fmt.Errorf("'%s' is not defined", dependency)
return nil, err
}

if _, found = definedTasks[dependency]; found {
err := fmt.Errorf("circular dependency detected, '%s' already added", dependency)
return nil, err
}

if _, found := visitedTasks[dependency]; !found && len(t.Dependencies) > 0 {
dependencies = append(t.Dependencies, dependencies...)
visitedTasks[dependency] = true
continue
}

dependencies = dependencies[1:]
definedTasks[dependency] = true

if len(t.Command) > 0 {
commands = append(commands, Command{name: t.Command[0], args: t.Command[1:], envs: t.Environments})
}
}

if len(task.Command) > 0 {
commands = append(commands, Command{name: task.Command[0], args: task.Command[1:], envs: task.Environments})
}

if len(task.Commands) > 0 {
for _, c := range task.Commands {
commands = append(commands, Command{name: c[0], args: c[1:], envs: task.Environments})
}
}

return commands, nil
}
101 changes: 4 additions & 97 deletions main.go
@@ -1,7 +1,6 @@
package main

import (
"bytes"
"flag"
"fmt"
"io"
Expand All @@ -10,9 +9,7 @@ import (
"runtime"
"sort"
"strings"
"text/template"

"github.com/BurntSushi/toml"
"github.com/y-yagi/bake/internal/log"
)

Expand All @@ -26,18 +23,6 @@ type Task struct {
Environments []string
}

// Command represents a command to run.
type Command struct {
name string
args []string
envs []string
}

// BakeFileVariable represents a variables of configuration file.
type BakeFileVariable struct {
OS string
}

var (
// Command line flags.
flags *flag.FlagSet
Expand Down Expand Up @@ -89,13 +74,14 @@ func run(args []string, stdout, stderr io.Writer) (exitCode int) {
return 0
}

tasks, err := parse(configFile)
config := NewConfig(configFile)
err := config.Parse()
if err != nil {
return msg(err, stderr)
}

if showTasksFlg {
showTasks(stdout, tasks)
showTasks(stdout, config.Tasks)
return 0
}

Expand All @@ -104,13 +90,7 @@ func run(args []string, stdout, stderr io.Writer) (exitCode int) {
target = flags.Args()[0]
}

task, found := tasks[target]
if !found {
err := fmt.Errorf("'%s' is not defined", target)
return msg(err, stderr)
}

commands, err := buildCommands(task, tasks)
commands, err := config.BuildCommands(target)
if err != nil {
return msg(err, stderr)
}
Expand All @@ -122,79 +102,6 @@ func run(args []string, stdout, stderr io.Writer) (exitCode int) {
return 0
}

func parse(configFile string) (map[string]Task, error) {
t, err := template.ParseFiles(configFile)
if err != nil {
return nil, err
}

parsedConfigFile := new(bytes.Buffer)
tv := BakeFileVariable{OS: runtime.GOOS}
if err = t.Execute(parsedConfigFile, tv); err != nil {
return nil, err
}

var p toml.Primitive
md, err := toml.Decode(parsedConfigFile.String(), &p)
if err != nil {
return nil, err
}

tasks := map[string]Task{}
if err := md.PrimitiveDecode(p, &tasks); err != nil {
return nil, err
}

return tasks, nil
}

func buildCommands(task Task, tasks map[string]Task) ([]Command, error) {
dependencies := task.Dependencies
definedTasks := map[string]bool{}
visitedTasks := map[string]bool{}
commands := []Command{}

for len(dependencies) > 0 {
dependency := dependencies[0]

t, found := tasks[dependency]
if !found {
err := fmt.Errorf("'%s' is not defined", dependency)
return nil, err
}

if _, found = definedTasks[dependency]; found {
err := fmt.Errorf("circular dependency detected, '%s' already added", dependency)
return nil, err
}

if _, found := visitedTasks[dependency]; !found && len(t.Dependencies) > 0 {
dependencies = append(t.Dependencies, dependencies...)
visitedTasks[dependency] = true
continue
}

dependencies = dependencies[1:]
definedTasks[dependency] = true

if len(t.Command) > 0 {
commands = append(commands, Command{name: t.Command[0], args: t.Command[1:], envs: t.Environments})
}
}

if len(task.Command) > 0 {
commands = append(commands, Command{name: task.Command[0], args: task.Command[1:], envs: task.Environments})
}

if len(task.Commands) > 0 {
for _, c := range task.Commands {
commands = append(commands, Command{name: c[0], args: c[1:], envs: task.Environments})
}
}

return commands, nil
}

func executeCommands(commands []Command, stdout, stderr io.Writer) error {
for _, command := range commands {
if dryRun {
Expand Down

0 comments on commit 7391711

Please sign in to comment.