Skip to content

Commit

Permalink
feat: initium project type flag (#101)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pehesi97 authored Oct 19, 2023
1 parent 9528449 commit 321141e
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 20 deletions.
2 changes: 2 additions & 0 deletions src/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func (c icli) baseBeforeFunc(ctx *cli.Context) error {
func (c *icli) init(cCtx *cli.Context) error {
appName := cCtx.String(appNameFlag)
version := cCtx.String(appVersionFlag)
projectType := cCtx.String(projectTypeFlag)
projectDirectory := cCtx.String(projectDirectoryFlag)
absProjectDirectory, err := filepath.Abs(cCtx.String(projectDirectoryFlag))
registry := cCtx.String(repoNameFlag)
Expand All @@ -91,6 +92,7 @@ func (c *icli) init(cCtx *cli.Context) error {

project := project.New(
appName,
project.ProjectType(projectType),
projectDirectory,
cCtx.String(runtimeVersionFlag),
version,
Expand Down
24 changes: 24 additions & 0 deletions src/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strconv"
"strings"

"github.com/charmbracelet/log"
"github.com/nearform/initium-cli/src/services/git"
"github.com/nearform/initium-cli/src/services/project"
"github.com/nearform/initium-cli/src/utils/defaults"
Expand Down Expand Up @@ -39,6 +40,7 @@ const (
appNameFlag string = "app-name"
appVersionFlag string = "app-version"
projectDirectoryFlag string = "project-directory"
projectTypeFlag string = "project-type"
repoNameFlag string = "container-registry"
dockerFileNameFlag string = "dockerfile-name"
configFileFlag string = "config-file"
Expand Down Expand Up @@ -70,6 +72,14 @@ func InitFlags() flags {
appName = *guess
}

var projectType project.ProjectType
tempProjectType, err := project.DetectType(".")
if err == nil {
projectType = tempProjectType
} else {
log.Warn(err)
}

f := flags{
requiredFlags: []string{},
all: map[FlagsType]([]cli.Flag){
Expand All @@ -79,6 +89,20 @@ func InitFlags() flags {
EnvVars: []string{"INITIUM_RUNTIME_VERSION"},
Category: "build",
},
&cli.StringFlag{
Name: projectTypeFlag,
Usage: "The project type (node, go)",
Value: string(projectType),
EnvVars: []string{"INITIUM_PROJECT_TYPE"},
Category: "build",
Required: projectType == "",
Action: func(ctx *cli.Context, value string) error {
if !project.IsValidProjectType(value) {
return fmt.Errorf("project type invalid, possible values are (node, go)")
}
return nil
},
},
},
Kubernetes: []cli.Flag{
&cli.StringFlag{
Expand Down
25 changes: 15 additions & 10 deletions src/cli/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,24 @@ import (
"testing"

"github.com/charmbracelet/log"
"github.com/nearform/initium-cli/src/services/project"
)

func compareConfig(t *testing.T, appName string, registry string, isPrivateService bool, writer io.Writer) {
func compareConfig(t *testing.T, appName string, registry string, isPrivateService bool, projectType project.ProjectType, writer io.Writer) {
configTemplate := fmt.Sprintf(`app-name: %s
container-registry: %s
default-branch: main
dockerfile-name: null
env-var-file: .env.initium
image-pull-secrets: null
private: %t
project-type: %s
runtime-version: null
`,
appName,
registry,
isPrivateService,
projectType,
)

result := fmt.Sprint(writer.(*bytes.Buffer))
Expand Down Expand Up @@ -74,32 +77,33 @@ func TestInitConfig(t *testing.T) {

registry := "ghcr.io/nearform"

if _, err := f.WriteString("app-name: FromFile\ncontainer-registry: " + registry); err != nil {
if _, err := f.WriteString("app-name: FromFile\ncontainer-registry: " + registry + "\nproject-type: go"); err != nil {
t.Errorf("writing config content %v", err)
}

reseticliBuffer(&icli)
if err = icli.Run([]string{"initium", fmt.Sprintf("--config-file=%s", f.Name()), "init", "config"}); err != nil {
t.Error(err)
}
compareConfig(t, "FromFile", registry, false, icli.Writer)
compareConfig(t, "FromFile", registry, false, "go", icli.Writer)

// Environment Variable wins over config
os.Setenv("INITIUM_APP_NAME", "FromEnv")
os.Setenv("INITIUM_PROJECT_TYPE", "go")
defer os.Unsetenv("INITIUM_PROJECT_TYPE")
defer os.Unsetenv("INITIUM_APP_NAME") // Unset the environment variable at the end
reseticliBuffer(&icli)
if err = icli.Run([]string{"initium", fmt.Sprintf("--config-file=%s", f.Name()), "init", "config"}); err != nil {
t.Error(err)
}
compareConfig(t, "FromEnv", registry, false, icli.Writer)
compareConfig(t, "FromEnv", registry, false, "go", icli.Writer)

// Command line argument wins over config and Environment variable
reseticliBuffer(&icli)
if err = icli.Run([]string{"initium", fmt.Sprintf("--config-file=%s", f.Name()), "init", "config", "--app-name=FromParam"}); err != nil {
if err = icli.Run([]string{"initium", fmt.Sprintf("--config-file=%s", f.Name()), "init", "config", "--app-name=FromParam", "--project-type=go"}); err != nil {
t.Error(err)
}
compareConfig(t, "FromParam", registry, false, icli.Writer)

compareConfig(t, "FromParam", registry, false, "go", icli.Writer)
}

func TestRepoNameRetrocompatibiliy(t *testing.T) {
Expand All @@ -113,22 +117,23 @@ func TestRepoNameRetrocompatibiliy(t *testing.T) {
defer f.Close()
defer os.Remove(f.Name())

if _, err := f.WriteString("repo-name: FromFile"); err != nil {
if _, err := f.WriteString("repo-name: FromFile\nproject-type: go"); err != nil {
t.Errorf("writing config content %v", err)
}

reseticliBuffer(&cli)
if err = cli.Run([]string{"initium", fmt.Sprintf("--config-file=%s", f.Name()), "init", "config", "--app-name=FromParam"}); err != nil {
t.Error(err)
}
compareConfig(t, "FromParam", "FromFile", false, cli.Writer)

compareConfig(t, "FromParam", "FromFile", false, "go", cli.Writer)

//Override from parameter
reseticliBuffer(&cli)
if err = cli.Run([]string{"initium", fmt.Sprintf("--config-file=%s", f.Name()), "init", "config", "--app-name=FromParam", "--container-registry=ghcr.io/nearform"}); err != nil {
t.Error(err)
}
compareConfig(t, "FromParam", "ghcr.io/nearform", false, cli.Writer)
compareConfig(t, "FromParam", "ghcr.io/nearform", false, "go", cli.Writer)
}

func TestAppName(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions src/cli/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func TestShouldRenderDockerTemplate(t *testing.T) {
Name: string(projectType),
Directory: path.Join(root, props["directory"]),
Resources: os.DirFS(root),
Type: projectType,
},
Writer: &buffer,
}
Expand Down
61 changes: 52 additions & 9 deletions src/services/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const (
type Project struct {
Name string
Version string
Type ProjectType
Directory string
RuntimeVersion string
DefaultRuntimeVersion string
Expand All @@ -45,9 +46,10 @@ func GuessAppName() *string {
return &name
}

func New(name string, directory string, runtimeVersion string, version string, isPrivate bool, imagePullSecrets []string, resources fs.FS) Project {
func New(name string, projectType ProjectType, directory string, runtimeVersion string, version string, isPrivate bool, imagePullSecrets []string, resources fs.FS) Project {
return Project{
Name: name,
Type: projectType,
Directory: directory,
RuntimeVersion: runtimeVersion,
ImagePullSecrets: imagePullSecrets,
Expand All @@ -57,20 +59,61 @@ func New(name string, directory string, runtimeVersion string, version string, i
}
}

func (proj *Project) detectType() (ProjectType, error) {
if _, err := os.Stat(path.Join(proj.Directory, "package.json")); err == nil {
func IsValidProjectType(projectType string) bool {
switch projectType {
case string(NodeProject):
return true
case string(GoProject):
return true
default:
return false
}
}

func DetectType(directory string) (ProjectType, error) {
var detectedRuntimes []ProjectType
var projectType ProjectType
if _, err := os.Stat(path.Join(directory, "package.json")); err == nil {
detectedRuntimes = append(detectedRuntimes, NodeProject)
projectType = NodeProject
}
if _, err := os.Stat(path.Join(directory, "go.mod")); err == nil {
detectedRuntimes = append(detectedRuntimes, GoProject)
projectType = GoProject
}
if len(detectedRuntimes) == 0 {
return "", fmt.Errorf("cannot detect the project type by checking the repository file structure, use the --project-type flag or the INITIUM_PROJECT_TYPE env variable to set the desired runtime")
}
if len(detectedRuntimes) > 1 {
return "", fmt.Errorf("more than one project runtimes detected (%v), use the --project-type flag or the INITIUM_PROJECT_TYPE env variable to set the desired runtime", detectedRuntimes)
}
return projectType, nil
}

func (proj *Project) setRuntimeVersion() error {
switch proj.Type {
case NodeProject:
proj.DefaultRuntimeVersion = defaults.DefaultNodeRuntimeVersion
return NodeProject, nil
} else if _, err := os.Stat(path.Join(proj.Directory, "go.mod")); err == nil {
return nil
case GoProject:
proj.DefaultRuntimeVersion = defaults.DefaultGoRuntimeVersion
return GoProject, nil
} else {
return "", fmt.Errorf("cannot detect project type %v", err)
return nil
default:
return fmt.Errorf("cannot detect runtime version for project type %s", proj.Type)
}
}

func (proj Project) loadDockerfile() ([]byte, error) {
projectType, err := proj.detectType()
var err error
projectType := proj.Type
if !IsValidProjectType(string(projectType)) {
projectType, err = DetectType(proj.Directory)
proj.Type = projectType
}
if err != nil {
return []byte{}, err
}
err = proj.setRuntimeVersion()
if err != nil {
return []byte{}, err
}
Expand Down
11 changes: 10 additions & 1 deletion src/services/project/project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,15 @@ func TestDetectType(t *testing.T) {
test_proj_type := Project{Name: string(project_type),
Directory: path.Join(root, props["directory"])}

proj_type, err := test_proj_type.detectType()
var proj_type ProjectType
var err error
if test_proj_type.Type == "" {
proj_type, err = DetectType(path.Join(root, props["directory"]))
test_proj_type.Type = proj_type
} else {
proj_type = test_proj_type.Type
err = nil
}

// if we cannot autodetect a project we will return an error
if project_type == "invalid" && err != nil {
Expand All @@ -46,6 +54,7 @@ func TestLoadDockerfile(t *testing.T) {
proj_dockerfile := Project{Name: string(project_type),
Directory: path.Join(root, props["directory"]),
Resources: os.DirFS(root),
Type: project_type,
}
_, err := proj_dockerfile.loadDockerfile()

Expand Down

0 comments on commit 321141e

Please sign in to comment.