<commit_before>package devstep

import (
	"bytes"
	"errors"
	"gopkg.in/yaml.v1"
	"os"
	"path/filepath"
	"text/template"
	"time"
)

type ConfigLoader interface {
	Load() (*ProjectConfig, error)
}

type configLoader struct {
	client        DockerClient
	homeDirectory string
	projectRoot   string
}

type yamlConfig struct {
	RepositoryName *string                 `yaml:"repository"`
	SourceImage    *string                 `yaml:"source_image"`
	CacheDir       *string                 `yaml:"cache_dir"`
	GuestDir       *string                 `yaml:"working_dir"`
	Privileged     *bool                   `yaml:"privileged"`
	Links          []string                `yaml:"links"`
	Volumes        []string                `yaml:"volumes"`
	Env            map[string]string       `yaml:"environment"`
	Hack           *yamlConfig             `yaml:"hack"`
	Commands       map[string]*yamlCommand `yaml:"commands"`
}

type yamlCommand struct {
	Name       *string           `yaml:"name"`
	Cmd        []string          `yaml:"cmd"`
	Privileged *bool             `yaml:"privileged"`
	Links      []string          `yaml:"links"`
	Volumes    []string          `yaml:"volumes"`
	Publish    []string          `yaml:"publish"`
	Env        map[string]string `yaml:"environment"`
}

func NewConfigLoader(client DockerClient, homeDirectory, projectRoot string) ConfigLoader {
	return &configLoader{
		client:        client,
		homeDirectory: homeDirectory,
		projectRoot:   projectRoot,
	}
}

func (l *configLoader) Load() (*ProjectConfig, error) {
	log.Info("Loading configuration for %s", l.projectRoot)

	config, err := l.buildDefaultConfig()
	if err != nil {
		return nil, err
	}

	yamlConf, err := parseYaml(l.homeDirectory + "/devstep.yml")
	if err != nil {
		return nil, err
	}
	if yamlConf != nil {
		log.Info("Loaded config from home dir")
		log.Debug("Home dir config: %+v", yamlConf)
		if yamlConf.RepositoryName != nil {
			return nil, errors.New("Repository name can't be set globally")
		}
		if yamlConf.Privileged != nil {
			return nil, errors.New("Privileged name can't be set globally")
		}
		assignYamlValues(yamlConf, config)
	}
	yamlConf, err = parseYaml(l.projectRoot + "/devstep.yml")
	if err != nil {
		return nil, err
	}
	if yamlConf != nil {
		log.Info("Loaded config from project dir")
		log.Debug("Project dir config: %+v", yamlConf)
		assignYamlValues(yamlConf, config)
		if yamlConf.Privileged != nil {
			config.Defaults.Privileged = yamlConf.Privileged
		}
	}

	tags, err := l.client.ListTags(config.RepositoryName)
	if err != nil {
		return nil, err
	}
	if len(tags) > 0 {
		config.BaseImage = config.RepositoryName + ":latest"
	} else {
		config.BaseImage = config.SourceImage
	}

	log.Info("Config loaded")
	log.Debug("Final config: %+v", config)

	return config, nil
}

func (l *configLoader) buildDefaultConfig() (*ProjectConfig, error) {
	projectDirName := filepath.Base(l.projectRoot)
	repositoryName := "devstep/" + projectDirName
	suffix := time.Now().Local().Format("20060102150405")

	config := &ProjectConfig{
		SourceImage:    "fgrehm/devstep:v0.3.1",
		RepositoryName: repositoryName,
		HostDir:        l.projectRoot,
		GuestDir:       "/workspace",
		CacheDir:       "/tmp/devstep/cache",
		Defaults: &DockerRunOpts{
			Name:     projectDirName + "-" + suffix,
			Env:      make(map[string]string),
			Hostname: projectDirName,
		},
		HackOpts: &DockerRunOpts{Env: make(map[string]string)},
		Commands: make(map[string]*ProjectCommand),
	}

	return config, nil
}

func parseYaml(configPath string) (*yamlConfig, error) {
	configInfo, err := os.Stat(configPath)<commit_msg>Bump default source image to 0.4.0<commit_after>package devstep

import (
	"bytes"
	"errors"
	"gopkg.in/yaml.v1"
	"os"
	"path/filepath"
	"text/template"
	"time"
)

type ConfigLoader interface {
	Load() (*ProjectConfig, error)
}

type configLoader struct {
	client        DockerClient
	homeDirectory string
	projectRoot   string
}

type yamlConfig struct {
	RepositoryName *string                 `yaml:"repository"`
	SourceImage    *string                 `yaml:"source_image"`
	CacheDir       *string                 `yaml:"cache_dir"`
	GuestDir       *string                 `yaml:"working_dir"`
	Privileged     *bool                   `yaml:"privileged"`
	Links          []string                `yaml:"links"`
	Volumes        []string                `yaml:"volumes"`
	Env            map[string]string       `yaml:"environment"`
	Hack           *yamlConfig             `yaml:"hack"`
	Commands       map[string]*yamlCommand `yaml:"commands"`
}

type yamlCommand struct {
	Name       *string           `yaml:"name"`
	Cmd        []string          `yaml:"cmd"`
	Privileged *bool             `yaml:"privileged"`
	Links      []string          `yaml:"links"`
	Volumes    []string          `yaml:"volumes"`
	Publish    []string          `yaml:"publish"`
	Env        map[string]string `yaml:"environment"`
}

func NewConfigLoader(client DockerClient, homeDirectory, projectRoot string) ConfigLoader {
	return &configLoader{
		client:        client,
		homeDirectory: homeDirectory,
		projectRoot:   projectRoot,
	}
}

func (l *configLoader) Load() (*ProjectConfig, error) {
	log.Info("Loading configuration for %s", l.projectRoot)

	config, err := l.buildDefaultConfig()
	if err != nil {
		return nil, err
	}

	yamlConf, err := parseYaml(l.homeDirectory + "/devstep.yml")
	if err != nil {
		return nil, err
	}
	if yamlConf != nil {
		log.Info("Loaded config from home dir")
		log.Debug("Home dir config: %+v", yamlConf)
		if yamlConf.RepositoryName != nil {
			return nil, errors.New("Repository name can't be set globally")
		}
		if yamlConf.Privileged != nil {
			return nil, errors.New("Privileged name can't be set globally")
		}
		assignYamlValues(yamlConf, config)
	}
	yamlConf, err = parseYaml(l.projectRoot + "/devstep.yml")
	if err != nil {
		return nil, err
	}
	if yamlConf != nil {
		log.Info("Loaded config from project dir")
		log.Debug("Project dir config: %+v", yamlConf)
		assignYamlValues(yamlConf, config)
		if yamlConf.Privileged != nil {
			config.Defaults.Privileged = yamlConf.Privileged
		}
	}

	tags, err := l.client.ListTags(config.RepositoryName)
	if err != nil {
		return nil, err
	}
	if len(tags) > 0 {
		config.BaseImage = config.RepositoryName + ":latest"
	} else {
		config.BaseImage = config.SourceImage
	}

	log.Info("Config loaded")
	log.Debug("Final config: %+v", config)

	return config, nil
}

func (l *configLoader) buildDefaultConfig() (*ProjectConfig, error) {
	projectDirName := filepath.Base(l.projectRoot)
	repositoryName := "devstep/" + projectDirName
	suffix := time.Now().Local().Format("20060102150405")

	config := &ProjectConfig{
		SourceImage:    "fgrehm/devstep:v0.4.0",
		RepositoryName: repositoryName,
		HostDir:        l.projectRoot,
		GuestDir:       "/workspace",
		CacheDir:       "/tmp/devstep/cache",
		Defaults: &DockerRunOpts{
			Name:     projectDirName + "-" + suffix,
			Env:      make(map[string]string),
			Hostname: projectDirName,
		},
		HackOpts: &DockerRunOpts{Env: make(map[string]string)},
		Commands: make(map[string]*ProjectCommand),
	}

	return config, nil
}

func parseYaml(configPath string) (*yamlConfig, error) {
	configInfo, err := os.Stat(configPath)
{'fraction_non_alphanumeric': 0.12840736128407362, 'fraction_numerical': 0.005673170056731701, 'mean_word_length': 3.106818181818182, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1325206', 'n_tokens_mistral': 2456, 'n_tokens_neox': 2295, 'n_words': 546}