Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Platformify Django projects #26

Merged
merged 11 commits into from
Mar 28, 2023
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ linters-settings:
check-type-assertions: true
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
# default is false: such cases aren't reported by default.
check-blank: true
check-blank: false
goconst:
# minimal occurrences count to trigger, 3 by default
min-occurrences: 5
Expand Down
8 changes: 4 additions & 4 deletions commands/platformify.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ services, choosing from a variety of stacks or simple runtimes.`,
&question.WorkingDirectory{},
&question.Stack{},
&question.Type{},
&question.DependencyManager{},
&question.Name{},
&question.ApplicationRoot{},
&question.Environment{},
&question.BuildSteps{},
&question.WebCommand{},
&question.ListenInterface{},
&question.DeployCommand{},
&question.DependencyManager{},
&question.Dependency{},
&question.ListenInterface{},
&question.WebCommand{},
&question.Services{},
&question.Dependency{},
)
err := q.AskQuestions(ctx)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/models/answer.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type Answers struct {
type Service struct {
Name string `json:"name"`
Type ServiceType `json:"type"`
Disk string `json:"disk"`
Disk string `json:"disk,omitempty"`
}

type RuntimeType struct {
Expand Down
29 changes: 28 additions & 1 deletion internal/question/application_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ package question

import (
"context"
"os"
"path"
"path/filepath"

"github.com/AlecAivazis/survey/v2"

"github.com/platformsh/platformify/internal/models"
"github.com/platformsh/platformify/internal/utils"
)

type ApplicationRoot struct{}
Expand All @@ -20,7 +24,30 @@ func (q *ApplicationRoot) Ask(ctx context.Context) error {
return nil
}

question := &survey.Input{Message: "Application root:"}
question := &survey.Input{Message: "Application root:", Default: "."}
cwd, _ := os.Getwd()
switch answers.DependencyManager {
case models.Composer:
if composerPath := utils.FindFile(cwd, "composer.json"); composerPath != "" {
question.Default, _ = filepath.Rel(cwd, path.Dir(composerPath))
}
case models.Npm, models.Yarn:
if packagePath := utils.FindFile(cwd, "package.json"); packagePath != "" {
question.Default, _ = filepath.Rel(cwd, path.Dir(packagePath))
}
case models.Poetry:
if pyProjectPath := utils.FindFile(cwd, "pyproject.toml"); pyProjectPath != "" {
question.Default, _ = filepath.Rel(cwd, path.Dir(pyProjectPath))
}
case models.Pipenv:
if pipfilePath := utils.FindFile(cwd, "Pipfile"); pipfilePath != "" {
question.Default, _ = filepath.Rel(cwd, path.Dir(pipfilePath))
}
case models.Pip:
if requirementsPath := utils.FindFile(cwd, "requirements.txt"); requirementsPath != "" {
question.Default, _ = filepath.Rel(cwd, path.Dir(requirementsPath))
}
}

var applicationRoot string
err := survey.AskOne(question, &applicationRoot, survey.WithValidator(survey.Required))
Expand Down
77 changes: 62 additions & 15 deletions internal/question/build_steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@ package question

import (
"context"
"fmt"
"os"
"path"
"path/filepath"

"github.com/AlecAivazis/survey/v2"

"github.com/platformsh/platformify/internal/models"
"github.com/platformsh/platformify/internal/utils"
)

const (
managePyFile = "manage.py"
)

type BuildSteps struct{}
Expand All @@ -20,30 +29,68 @@ func (q *BuildSteps) Ask(ctx context.Context) error {
return nil
}

for {
var question survey.Prompt
var addStep = true
question = &survey.Confirm{
Message: "Do you want to add a build step?",
Default: true,
if answers.Stack == models.Django {
prefix := ""
switch answers.DependencyManager {
case models.Poetry:
answers.BuildSteps = append(
answers.BuildSteps,
"# Set PIP_USER to 0 so that poetry does not complain",
"export PIP_USER=0",
"# Install poetry as a global tool",
"python -m venv /app/.global",
"pip install poetry==$POETRY_VERSION",
"poetry install",
)
prefix = "poetry run "
case models.Pipenv:
answers.BuildSteps = append(
answers.BuildSteps,
"# Set PIP_USER to 0 so that Pipenv does not complain",
"export PIP_USER=0",
"# Install Pipenv as a global tool",
"python -m venv /app/.global",
"pip install poetry==$PIPENV_VERSION",
"pipenv install",
)
prefix = "pipenv run "
case models.Pip:
answers.BuildSteps = append(
answers.BuildSteps,
"pip install -r requirements.txt",
akalipetis marked this conversation as resolved.
Show resolved Hide resolved
)
}

err := survey.AskOne(question, &addStep)
if err != nil {
return err
cwd, _ := os.Getwd()
if managePyPath := utils.FindFile(path.Join(cwd, answers.ApplicationRoot), managePyFile); managePyPath != "" {
managePyPath, _ = filepath.Rel(path.Join(cwd, answers.ApplicationRoot), managePyPath)
answers.BuildSteps = append(
answers.BuildSteps,
"# Collect static files so that they can be served by Platform.sh",
fmt.Sprintf("%spython %s collectstatic --noinput", prefix, managePyPath),
)
}
}

if !addStep {
break
if len(answers.BuildSteps) > 0 {
fmt.Println("We identified a few build steps for you already!")
for _, step := range answers.BuildSteps {
fmt.Println(" " + step)
}
}

question = &survey.Input{Message: "Build step:"}

for {
var step string
err = survey.AskOne(question, &step)
if err != nil {
keyPrompt := survey.Input{Message: "Add a build step (leave blank to skip)"}
if len(answers.BuildSteps) > 0 {
keyPrompt.Message = "Add another build step (leave blank to skip)"
}
if err := survey.AskOne(&keyPrompt, &step, nil); err != nil {
return err
}
if step == "" {
break
}

answers.BuildSteps = append(answers.BuildSteps, step)
}
Expand Down
71 changes: 29 additions & 42 deletions internal/question/dependency_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ import (
"github.com/AlecAivazis/survey/v2"

"github.com/platformsh/platformify/internal/models"
"github.com/platformsh/platformify/internal/utils"
)

const (
npmLockFileName = "package-lock.json"
yarnLockFileName = "yarn.lock"
poetryLockFile = "poetry.lock"
pipenvLockFile = "Pipfile.lock"
pipLockFile = "requirements.txt"
)

type DependencyManager struct{}
Expand All @@ -26,34 +30,6 @@ func (q *DependencyManager) Ask(ctx context.Context) error {
return nil
}

switch answers.Type.Runtime {
case models.Python:
// TODO: check for python dependency manager
case models.PHP:
// TODO: check for php dependency manager
case models.NodeJS:
// Check if the project uses "npm" as a dependency manager
exists, err := fileExists(npmLockFileName)
if err != nil {
return err
} else if exists {
answers.DependencyManager = models.Npm
return nil
}

// Check if the project uses "yarn" as a dependency manager
exists, err = fileExists(yarnLockFileName)
if err != nil {
return err
} else if exists {
answers.DependencyManager = models.Yarn
return nil
}
default:
// Skip the step
return nil
}

// We couldn't define the dependency manager automatically, so ask the user
depManagers := models.DepManagersMap.Titles(answers.Type.Runtime)
if len(depManagers) == 0 {
Expand All @@ -66,9 +42,32 @@ func (q *DependencyManager) Ask(ctx context.Context) error {
Options: depManagers,
}

if cwd, err := os.Getwd(); err == nil {
switch answers.Type.Runtime {
case models.Python:
if exists := utils.FileExists(cwd, poetryLockFile); exists {
question.Default = models.Poetry.Title()
} else if exists := utils.FileExists(cwd, pipenvLockFile); exists {
question.Default = models.Pipenv.Title()
} else if exists := utils.FileExists(cwd, pipLockFile); exists {
question.Default = models.Pip.Title()
}
case models.PHP:
// TODO: check for php dependency manager
case models.NodeJS:
if exists := utils.FileExists(cwd, npmLockFileName); exists {
question.Default = models.Npm.Title()
} else if exists := utils.FileExists(cwd, yarnLockFileName); exists {
question.Default = models.Yarn.Title()
}
default:
// Skip the step
return nil
}
}

var title string
err := survey.AskOne(question, &title)
if err != nil {
if err := survey.AskOne(question, &title); err != nil {
return err
}

Expand All @@ -80,15 +79,3 @@ func (q *DependencyManager) Ask(ctx context.Context) error {

return nil
}

// fileExists checks if the file exists
func fileExists(name string) (bool, error) {
_, err := os.Stat(name)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
return true, nil
}
19 changes: 19 additions & 0 deletions internal/question/deploy_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ package question

import (
"context"
"fmt"
"os"
"path"
"path/filepath"

"github.com/AlecAivazis/survey/v2"

"github.com/platformsh/platformify/internal/models"
"github.com/platformsh/platformify/internal/utils"
)

type DeployCommand struct{}
Expand All @@ -21,6 +26,20 @@ func (q *DeployCommand) Ask(ctx context.Context) error {
}

question := &survey.Input{Message: "Deploy command:"}
cwd, _ := os.Getwd()
if answers.Stack == models.Django {
if managePyPath := utils.FindFile(path.Join(cwd, answers.ApplicationRoot), managePyFile); managePyPath != "" {
managePyPath, _ = filepath.Rel(path.Join(cwd, answers.ApplicationRoot), managePyPath)
prefix := ""
switch answers.DependencyManager {
case models.Pipenv:
prefix = "pipenv run "
case models.Poetry:
prefix = "poetry run "
}
question.Default = fmt.Sprintf("%spython %s migrate", prefix, managePyPath)
}
}

var command string
err := survey.AskOne(question, &command)
Expand Down
Loading