Skip to content

Commit

Permalink
Merge pull request #18 from ThreeDotsLabs/create-dir-with-init
Browse files Browse the repository at this point in the history
"tdl tr init" creates new directory
  • Loading branch information
roblaszczak authored Sep 11, 2024
2 parents 02de1de + 3e415c8 commit 1ad4934
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 42 deletions.
2 changes: 1 addition & 1 deletion trainings/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (h *Handlers) Clone(ctx context.Context, executionID string) error {
"err": err,
}).Debug("Received exercise from server")

if err := h.startTraining(ctx, resp.TrainingName); err != nil {
if _, err := h.startTraining(ctx, resp.TrainingName, false); err != nil {
return err
}

Expand Down
2 changes: 1 addition & 1 deletion trainings/config/training.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,5 @@ func (c Config) FindTrainingRoot() (string, error) {
previousDir = dir
}

return "", TrainingRootNotFoundError
return "", errors.WithStack(TrainingRootNotFoundError)
}
99 changes: 70 additions & 29 deletions trainings/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"os/exec"
"path"
"path/filepath"
"strings"

"github.com/ThreeDotsLabs/cli/internal"
Expand All @@ -23,46 +24,81 @@ func (h *Handlers) Init(ctx context.Context, trainingName string) error {
"training_name": trainingName,
}).Debug("Starting training")

if err := h.startTraining(ctx, trainingName); errors.Is(err, ErrInterrupted) {
trainingRoot, err := h.startTraining(ctx, trainingName, true)
if errors.Is(err, ErrInterrupted) {
fmt.Println("Interrupted")
return nil
} else if err != nil {
return err
}

// todo - handle situation when training was started but something failed here and someone is starting excersise again (because he have no local files)
_, err := h.nextExercise(ctx, "")
_, err = h.nextExercise(ctx, "", trainingRoot)
if err != nil {
return err
}

fmt.Println("To see exercise content, please go back to " + color.CyanString(internal.WebsiteAddress))
if !isInTrainingRoot(trainingRoot) {
fmt.Println("\nNow run " + color.CyanString("cd "+trainingName+"/") + " to enter the training workspace")
}

return nil
}

func isInTrainingRoot(trainingRoot string) bool {
pwd, err := os.Getwd()
if err != nil {
logrus.WithError(err).Warn("Can't get current working directory")
return false
}

absPwd, err := filepath.Abs(pwd)
if err != nil {
logrus.WithError(err).Warn("Can't get absolute path of current working directory")
return false
}

absTrainingRoot, err := filepath.Abs(trainingRoot)
if err != nil {
logrus.WithError(err).Warn("Can't get absolute path of training root")
return false
}

return absPwd == absTrainingRoot
}

var ErrInterrupted = errors.New("interrupted")

func (h *Handlers) startTraining(ctx context.Context, trainingName string) error {
func (h *Handlers) startTraining(
ctx context.Context,
trainingName string,
addTrainingNameToDir bool,
) (string, error) {
var trainingRoot string

alreadyExistingTrainingRoot, err := h.config.FindTrainingRoot()
if err == nil {
fmt.Println(color.BlueString("Training was already initialised. Training root:" + alreadyExistingTrainingRoot))
trainingRoot = alreadyExistingTrainingRoot
} else if !errors.Is(err, config.TrainingRootNotFoundError) {
return errors.Wrap(err, "can't check if training root exists")
return "", errors.Wrap(err, "can't check if training root exists")
} else {
if err := h.showTrainingStartPrompt(); err != nil {
return err
}

wd, err := os.Getwd()
if err != nil {
return errors.WithStack(err)
return "", errors.WithStack(err)
}

if addTrainingNameToDir {
trainingRoot = path.Join(wd, trainingName)
} else {
trainingRoot = wd
}

if err := h.showTrainingStartPrompt(trainingRoot); err != nil {
return "", err
}

// we will create training root in current working directory
trainingRoot = wd
logrus.Debug("No training root yet")
}

Expand All @@ -71,13 +107,18 @@ func (h *Handlers) startTraining(ctx context.Context, trainingName string) error
if alreadyExistingTrainingRoot != "" {
cfg := h.config.TrainingConfig(trainingRootFs)
if cfg.TrainingName != trainingName {
return fmt.Errorf(
return "", fmt.Errorf(
"training %s was already started in this directory, please go to other directory and run `tdl training init`",
cfg.TrainingName,
)
}
} else {
err = createGoWorkspace(trainingRoot)
err := os.MkdirAll(trainingRoot, 0755)
if err != nil {
return "", errors.Wrap(err, "can't create training root dir")
}

err = createGoWorkspace(trainingRoot, trainingName)
if err != nil {
logrus.WithError(err).Warn("Could not create go workspace")
}
Expand All @@ -91,18 +132,18 @@ func (h *Handlers) startTraining(ctx context.Context, trainingName string) error
},
)
if err != nil {
return errors.Wrap(err, "start training gRPC call failed")
return "", errors.Wrap(err, "start training gRPC call failed")
}

if err := h.config.WriteTrainingConfig(config.TrainingConfig{TrainingName: trainingName}, trainingRootFs); err != nil {
return errors.Wrap(err, "can't write training config")
return "", errors.Wrap(err, "can't write training config")
}

if err := writeGitignore(trainingRootFs); err != nil {
return err
return "", err
}

return nil
return trainingRoot, nil
}

var gitignore = strings.Join(
Expand All @@ -129,14 +170,19 @@ func writeGitignore(trainingRootFs *afero.BasePathFs) error {
return nil
}

func createGoWorkspace(trainingRoot string) error {
func createGoWorkspace(trainingRoot, trainingName string) error {
cmd := exec.Command("go", "work", "init")
cmd.Dir = trainingRoot

printlnCommand(".", "go work init")
printlnCommand(trainingRoot, "go work init")

if err := cmd.Run(); err != nil {
return errors.Wrap(err, "can't run go work init")
out, err := cmd.CombinedOutput()
if strings.Contains(string(out), "already exists") {
logrus.Debug("go.work already exists")
return nil
}
if err != nil {
return errors.Wrapf(err, "can't run go work init: %s", string(out))
}

return nil
Expand All @@ -155,7 +201,7 @@ func addModuleToWorkspace(trainingRoot string, modulePath string) error {
cmd := exec.Command("go", "work", "use", modulePath)
cmd.Dir = trainingRoot

printlnCommand(".", fmt.Sprintf("go work use %v", modulePath))
printlnCommand(trainingRoot, fmt.Sprintf("go work use %v", modulePath))

if err := cmd.Run(); err != nil {
return errors.Wrap(err, "can't run go work use")
Expand All @@ -164,15 +210,10 @@ func addModuleToWorkspace(trainingRoot string, modulePath string) error {
return nil
}

func (h *Handlers) showTrainingStartPrompt() error {
pwd, err := os.Getwd()
if err != nil {
return errors.Wrap(err, "can't get wd")
}

func (h *Handlers) showTrainingStartPrompt(trainingDir string) error {
fmt.Printf(
"This command will clone training source code to %s directory.\n",
pwd,
trainingDir,
)

if !internal.ConfirmPromptDefaultYes("continue") {
Expand Down
7 changes: 1 addition & 6 deletions trainings/next.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,9 @@ import (
"github.com/spf13/afero"
)

func (h *Handlers) nextExercise(ctx context.Context, currentExerciseID string) (finished bool, err error) {
func (h *Handlers) nextExercise(ctx context.Context, currentExerciseID string, trainingRoot string) (finished bool, err error) {
h.solutionHintDisplayed = false

trainingRoot, err := h.config.FindTrainingRoot()
if err != nil {
return false, err
}

// We should never trust the remote server.
// Writing files based on external name is a vector for Path Traversal attack.
// For more info please check: https://owasp.org/www-community/attacks/Path_Traversal
Expand Down
7 changes: 6 additions & 1 deletion trainings/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import (
)

func (h *Handlers) Reset(ctx context.Context) error {
_, err := h.nextExercise(ctx, "")
trainingRoot, err := h.config.FindTrainingRoot()
if err != nil {
return err
}

_, err = h.nextExercise(ctx, "", trainingRoot)
if err != nil {
return err
}
Expand Down
39 changes: 35 additions & 4 deletions trainings/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,12 @@ func (h *Handlers) detachedRun(ctx context.Context, trainingRootFs *afero.BasePa
os.Exit(0)
}

finished, err := h.nextExercise(ctx, h.config.ExerciseConfig(trainingRootFs).ExerciseID)
trainingRoot, err := h.config.FindTrainingRoot()
if err != nil {
return err
}

finished, err := h.nextExercise(ctx, h.config.ExerciseConfig(trainingRootFs).ExerciseID, trainingRoot)
if err != nil {
return err
}
Expand Down Expand Up @@ -122,7 +127,12 @@ func (h *Handlers) interactiveRun(ctx context.Context, trainingRootFs *afero.Bas
continue
}

finished, err := h.nextExercise(ctx, h.config.ExerciseConfig(trainingRootFs).ExerciseID)
trainingRoot, err := h.config.FindTrainingRoot()
if err != nil {
return err
}

finished, err := h.nextExercise(ctx, h.config.ExerciseConfig(trainingRootFs).ExerciseID, trainingRoot)
if err != nil {
return err
}
Expand Down Expand Up @@ -154,7 +164,12 @@ func (h *Handlers) run(ctx context.Context, trainingRootFs *afero.BasePathFs) (b
os.Exit(0)
}

_, err = h.nextExercise(ctx, "")
trainingRoot, err := h.config.FindTrainingRoot()
if err != nil {
return false, err
}

_, err = h.nextExercise(ctx, "", trainingRoot)
return true, err
}

Expand Down Expand Up @@ -271,7 +286,23 @@ func printCommand(root string, command string) {
}

func printlnCommand(root string, command string) {
printCommand(root, command+"\n")
pwd, err := os.Getwd()
if err != nil {
fmt.Println("Error getting current directory:", err)
return
}

relPath, err := filepath.Rel(pwd, root)
if err != nil {
fmt.Println("Error getting relative path:", err)
return
}

if relPath != "." && relPath != "" {
relPath = "./" + relPath
}

printCommand(relPath, command+"\n")
}

func (h *Handlers) generateRunTerminalPath(trainingRootFs *afero.BasePathFs) string {
Expand Down

0 comments on commit 1ad4934

Please sign in to comment.