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

feat(cmd): add next steps hint after template install #417

Merged
merged 4 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 30 additions & 14 deletions cmd/lk/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,17 @@ var (
{
Name: "install",
Usage: "Execute installation defined in " + bootstrap.TaskFile,
ArgsUsage: "`DIR` location or the project directory (default: current directory)",
ArgsUsage: "[DIR] location of the project directory (default: current directory)",
Before: requireProject,
Action: installTemplate,
},
{
Hidden: true,
Name: "run",
Usage: "Execute a task defined in " + bootstrap.TaskFile,
ArgsUsage: "[TASK] to run in the project's taskfile.yaml",
Action: runTask,
},
Comment on lines +95 to +101
Copy link
Contributor Author

@rektdeckard rektdeckard Sep 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidzhao hiding this command for now. my other idea was to have a post_create task or smth that runs after we clone, degit, and inflate the environment. All it would do is print the steps to install and get started:

tobiasfried@slate /tmp % lk app sandbox --id 5lfwjtkd6cy
Using default project [Atrium]
Cloning template...
Instantiating environment...
Done! To setup and run your sandbox:

   cd /tmp/foo
   python3 -m venv venv
   source venv/bin/activate
   python3 -m pip install -r requirements.txt
   python3 agent.py start

tobiasfried@slate /tmp %

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great, I think this is perfect!

{
Name: "env",
Usage: "Manage environment variables",
Expand All @@ -100,12 +107,6 @@ var (
return instantiateEnv(ctx, cmd, ".")
},
},
{
Name: "run",
Usage: "Execute a task defined in " + bootstrap.TaskFile,
ArgsUsage: "`DIR` location or the project directory (default: current directory)",
Action: runTask,
},
},
},
}
Expand Down Expand Up @@ -253,7 +254,7 @@ func setupSandboxTemplate(ctx context.Context, cmd *cli.Command) error {
return errors.New("sandbox ID is required")
}

_, token, err := requireToken(ctx, cmd)
token, err := requireToken(ctx, cmd)
if err != nil {
return err
}
Expand Down Expand Up @@ -364,25 +365,40 @@ func doInstall(ctx context.Context, task bootstrap.KnownTask, rootPath string, v

fullPath, err := filepath.Abs(rootPath)
if fullPath != "" {
fmt.Println("Installed template to " + fullPath)
fmt.Println("Installed template to " + fullPath + ". To start your sandbox:\n")
fmt.Println(" cd " + fullPath)
fmt.Println(" lk app run dev_sandbox")
fmt.Println("")
}

return err
}

func runTask(ctx context.Context, cmd *cli.Command) error {
verbose := cmd.Bool("verbose")
taskName := cmd.Args().First()
if taskName == "" {
return errors.New("task name is required")
}

rootDir := "."
tf, err := bootstrap.ParseTaskfile(rootDir)
if err != nil {
return err
}

taskName := cmd.Args().First()
if taskName == "" {
var options []huh.Option[string]
for _, name := range tf.Tasks.Keys() {
options = append(options, huh.NewOption(name, name))
}

if err := huh.NewSelect[string]().
Title("Select Task").
Options(options...).
Value(&taskName).
WithTheme(theme).
Run(); err != nil {
return err
}
}

task, err := bootstrap.NewTask(ctx, tf, rootDir, taskName, cmd.Bool("verbose"))
if err != nil {
return err
Expand Down
25 changes: 14 additions & 11 deletions cmd/lk/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,35 +230,38 @@ func handleAuth(ctx context.Context, cmd *cli.Command) error {
if err := loadProjectConfig(ctx, cmd); err != nil {
return err
}
cfg, token, err := requireToken(ctx, cmd)
token, err := requireToken(ctx, cmd)
if err != nil {
return err
}
return authClient.Deauthenticate(ctx, cfg.Name, token)
return authClient.Deauthenticate(ctx, project.Name, token)
}
return tryAuthIfNeeded(ctx, cmd)
}

func requireToken(_ context.Context, cmd *cli.Command) (*config.ProjectConfig, string, error) {
cfg, err := loadProjectDetails(cmd)
if err != nil {
return nil, "", err
func requireToken(_ context.Context, cmd *cli.Command) (string, error) {
if project == nil {
var err error
project, err = loadProjectDetails(cmd)
if err != nil {
return "", err
}
}

// construct a token from the chosen project, using the hashed secret as the identity
// as a means of preventing any old token generated with this key/secret pair from
// deleting it
hash, err := hashString(cfg.APISecret)
hash, err := hashString(project.APISecret)
if err != nil {
return nil, "", err
return "", err
}
at := auth.NewAccessToken(cfg.APIKey, cfg.APISecret).SetIdentity(hash)
at := auth.NewAccessToken(project.APIKey, project.APISecret).SetIdentity(hash)
token, err := at.ToJWT()
if err != nil {
return nil, "", err
return "", err
}

return cfg, token, nil
return token, nil
}

func tryAuthIfNeeded(ctx context.Context, cmd *cli.Command) error {
Expand Down
1 change: 0 additions & 1 deletion cmd/lk/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ func main() {
go func() {
<-ctx.Done()
stop()
fmt.Println()
}()

checkForLegacyName()
Expand Down
6 changes: 2 additions & 4 deletions cmd/lk/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"errors"
"fmt"
"net/url"
"os"
"regexp"

"github.com/charmbracelet/huh"
Expand Down Expand Up @@ -243,10 +242,9 @@ func listProjects(ctx context.Context, cmd *cli.Command) error {
return nil
}

re := lipgloss.NewRenderer(os.Stdout)
baseStyle := re.NewStyle().Padding(0, 1)
baseStyle := theme.Form.Foreground(fg).Padding(0, 1)
headerStyle := baseStyle.Bold(true)
selectedStyle := baseStyle.Foreground(cyan)
selectedStyle := theme.Focused.Title.Padding(0, 1)

table := CreateTable().
StyleFunc(func(row, col int) lipgloss.Style {
Expand Down
42 changes: 2 additions & 40 deletions cmd/lk/style.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,47 +20,9 @@ import (
)

var (
normalFg = lipgloss.AdaptiveColor{Light: "235", Dark: "252"}
normalBg = lipgloss.AdaptiveColor{Light: "20", Dark: "0"}
dimFg = lipgloss.AdaptiveColor{Light: "", Dark: "243"}
placeholderFg = lipgloss.AdaptiveColor{Light: "248", Dark: "238"}
cyan = lipgloss.AdaptiveColor{Light: "#06B7DB", Dark: "#1FD5F9"}
red = lipgloss.AdaptiveColor{Light: "#CE4A3B", Dark: "#FF6352"}
yellow = lipgloss.AdaptiveColor{Light: "#DB9406", Dark: "#F9B11F"}
green = lipgloss.AdaptiveColor{Light: "#036D26", Dark: "#06DB4D"}

fg = lipgloss.AdaptiveColor{Light: "235", Dark: "252"}
theme = func() *huh.Theme {
t := huh.ThemeBase()

t.Focused.Base = t.Focused.Base.BorderForeground(lipgloss.Color("238"))
t.Focused.Title = t.Focused.Title.Foreground(cyan).Bold(true)
t.Focused.NoteTitle = t.Focused.NoteTitle.Foreground(cyan).Bold(true).MarginBottom(1)
t.Focused.Directory = t.Focused.Directory.Foreground(cyan)
t.Focused.Description = t.Focused.Description.Foreground(dimFg)
t.Focused.ErrorIndicator = t.Focused.ErrorIndicator.Foreground(red)
t.Focused.ErrorMessage = t.Focused.ErrorMessage.Foreground(red)
t.Focused.SelectSelector = t.Focused.SelectSelector.Foreground(yellow)
t.Focused.NextIndicator = t.Focused.NextIndicator.Foreground(yellow)
t.Focused.PrevIndicator = t.Focused.PrevIndicator.Foreground(yellow)
t.Focused.Option = t.Focused.Option.Foreground(normalFg)
t.Focused.MultiSelectSelector = t.Focused.MultiSelectSelector.Foreground(yellow)
t.Focused.SelectedOption = t.Focused.SelectedOption.Foreground(green)
t.Focused.SelectedPrefix = lipgloss.NewStyle().Foreground(green).SetString("✓ ")
t.Focused.UnselectedPrefix = lipgloss.NewStyle().Foreground(dimFg).SetString("• ")
t.Focused.UnselectedOption = t.Focused.UnselectedOption.Foreground(normalFg)
t.Focused.FocusedButton = t.Focused.FocusedButton.Foreground(normalBg).Background(cyan)
t.Focused.Next = t.Focused.FocusedButton
t.Focused.BlurredButton = t.Focused.BlurredButton.Foreground(normalFg).Background(lipgloss.AdaptiveColor{Light: "252", Dark: "237"})

// t.Focused.TextInput.Cursor = t.Focused.TextInput.Cursor.Foreground(yellow)
t.Focused.TextInput.Placeholder = t.Focused.TextInput.Placeholder.Foreground(placeholderFg)
t.Focused.TextInput.Prompt = t.Focused.TextInput.Prompt.Foreground(yellow)

t.Blurred = t.Focused
t.Blurred.Base = t.Focused.Base.BorderStyle(lipgloss.HiddenBorder())
t.Blurred.NextIndicator = lipgloss.NewStyle()
t.Blurred.PrevIndicator = lipgloss.NewStyle()

t := huh.ThemeBase16()
return t
}()
)
7 changes: 3 additions & 4 deletions cmd/lk/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,7 @@ func PrintJSON(obj any) {
}

func CreateTable() *table.Table {
re := lipgloss.NewRenderer(os.Stdout)
baseStyle := re.NewStyle().Padding(0, 1)
baseStyle := theme.Form.Foreground(fg).Padding(0, 1)
headerStyle := baseStyle.Bold(true)

styleFunc := func(row, col int) lipgloss.Style {
Expand All @@ -179,7 +178,7 @@ func CreateTable() *table.Table {

t := table.New().
Border(lipgloss.NormalBorder()).
BorderStyle(re.NewStyle().Foreground(normalFg)).
BorderStyle(theme.Form.Foreground(fg)).
StyleFunc(styleFunc)

return t
Expand Down Expand Up @@ -266,7 +265,7 @@ func loadProjectDetails(c *cli.Command, opts ...loadOption) (*config.ProjectConf
// load default project
dp, err := config.LoadDefaultProject()
if err == nil {
fmt.Println("Using default project", dp.Name)
fmt.Println("Using default project [" + theme.Focused.Title.Render(dp.Name) + "]")
logDetails(c, dp)
return dp, nil
}
Expand Down
Loading