fixes
This commit is contained in:
294
main.go
294
main.go
@@ -16,120 +16,222 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "pb",
|
||||
Usage: "PocketBase deployment helper",
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "init",
|
||||
Usage: "start a new PocketBase project",
|
||||
Action: initAction(),
|
||||
},
|
||||
{
|
||||
Name: "dev",
|
||||
Usage: "run the PocketBase binary locally",
|
||||
Action: devAction(),
|
||||
},
|
||||
{
|
||||
Name: "deploy",
|
||||
Usage: "deploy the PocketBase project",
|
||||
Action: placeholderAction("deploy"),
|
||||
},
|
||||
{
|
||||
Name: "logs",
|
||||
Usage: "show PocketBase logs",
|
||||
Action: placeholderAction("logs"),
|
||||
},
|
||||
{
|
||||
Name: "secrets",
|
||||
Usage: "manage deployment secrets",
|
||||
Action: placeholderAction("secrets"),
|
||||
},
|
||||
},
|
||||
commands := defaultCommands()
|
||||
args := os.Args[1:]
|
||||
if len(args) > 0 {
|
||||
if args[0] == "--" {
|
||||
args = args[1:]
|
||||
}
|
||||
if len(args) > 0 {
|
||||
if err := runCommandByName(commands, args[0]); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
if err := tea.NewProgram(newModel()).Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func placeholderAction(name string) func(*cli.Context) error {
|
||||
return func(c *cli.Context) error {
|
||||
func defaultCommands() []command {
|
||||
return []command{
|
||||
{name: "init", description: "start a new PocketBase project", action: runInit},
|
||||
{name: "dev", description: "run the PocketBase binary locally", action: runDev},
|
||||
{name: "deploy", description: "deploy the PocketBase project", action: placeholderAction("deploy")},
|
||||
{name: "logs", description: "show PocketBase logs", action: placeholderAction("logs")},
|
||||
{name: "secrets", description: "manage deployment secrets", action: placeholderAction("secrets")},
|
||||
}
|
||||
}
|
||||
|
||||
type commandAction func() error
|
||||
|
||||
type command struct {
|
||||
name string
|
||||
description string
|
||||
action commandAction
|
||||
}
|
||||
|
||||
func runCommandByName(commands []command, name string) error {
|
||||
for _, cmd := range commands {
|
||||
if cmd.name == name {
|
||||
return cmd.action()
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("unknown command %q", name)
|
||||
}
|
||||
|
||||
func newModel() model {
|
||||
return model{commands: defaultCommands()}
|
||||
}
|
||||
|
||||
type model struct {
|
||||
commands []command
|
||||
cursor int
|
||||
status string
|
||||
running bool
|
||||
lastCommand string
|
||||
}
|
||||
|
||||
type commandResultMsg struct {
|
||||
name string
|
||||
err error
|
||||
}
|
||||
|
||||
func (m model) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "ctrl+c", "q":
|
||||
return m, tea.Quit
|
||||
case "up", "k":
|
||||
if m.running {
|
||||
return m, nil
|
||||
}
|
||||
if m.cursor > 0 {
|
||||
m.cursor--
|
||||
} else {
|
||||
m.cursor = len(m.commands) - 1
|
||||
}
|
||||
return m, nil
|
||||
case "down", "j":
|
||||
if m.running {
|
||||
return m, nil
|
||||
}
|
||||
if m.cursor < len(m.commands)-1 {
|
||||
m.cursor++
|
||||
} else {
|
||||
m.cursor = 0
|
||||
}
|
||||
return m, nil
|
||||
case "enter":
|
||||
if m.running {
|
||||
return m, nil
|
||||
}
|
||||
selected := m.commands[m.cursor]
|
||||
m.running = true
|
||||
m.lastCommand = selected.name
|
||||
m.status = fmt.Sprintf("Running %s...", selected.name)
|
||||
return m, runCommand(selected)
|
||||
}
|
||||
case commandResultMsg:
|
||||
m.running = false
|
||||
if msg.err != nil {
|
||||
m.status = fmt.Sprintf("%s failed: %v", msg.name, msg.err)
|
||||
} else {
|
||||
m.status = fmt.Sprintf("%s finished successfully", msg.name)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m model) View() string {
|
||||
var b strings.Builder
|
||||
b.WriteString("PocketBase deployment helper\n\n")
|
||||
for idx, cmd := range m.commands {
|
||||
cursor := " "
|
||||
if m.cursor == idx {
|
||||
cursor = ">"
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("%s %s\n %s\n", cursor, cmd.name, cmd.description))
|
||||
}
|
||||
b.WriteString("\n")
|
||||
if m.status != "" {
|
||||
b.WriteString(fmt.Sprintf("%s\n\n", m.status))
|
||||
}
|
||||
if m.running {
|
||||
b.WriteString("(running... press ctrl+c to abort)\n")
|
||||
}
|
||||
b.WriteString("↑/↓ to navigate, enter to run, q to quit\n")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func runCommand(cmd command) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
return commandResultMsg{name: cmd.name, err: cmd.action()}
|
||||
}
|
||||
}
|
||||
|
||||
func placeholderAction(name string) commandAction {
|
||||
return func() error {
|
||||
fmt.Printf("TODO: implement %s command\n", name)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func devAction() func(*cli.Context) error {
|
||||
return func(c *cli.Context) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
binaryPath := filepath.Join(cwd, "bin", pocketbaseBinaryName(runtime.GOOS))
|
||||
if _, err := os.Stat(binaryPath); err != nil {
|
||||
return fmt.Errorf("cannot find PocketBase binary at %s: %w", binaryPath, err)
|
||||
}
|
||||
|
||||
overrides, err := loadEnv(filepath.Join(cwd, ".env"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command(binaryPath, "serve")
|
||||
cmd.Dir = cwd
|
||||
cmd.Env = mergeEnv(overrides)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
|
||||
fmt.Println("starting PocketBase development server")
|
||||
return cmd.Run()
|
||||
func runInit() error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pbPath := filepath.Join(cwd, "pb.toml")
|
||||
if _, err := os.Stat(pbPath); err == nil {
|
||||
return fmt.Errorf("pb.toml already exists, aborting")
|
||||
} else if !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
serviceName := generateServiceName()
|
||||
|
||||
if err := writePBConfig(pbPath, serviceName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targetDir := filepath.Join(cwd, "bin")
|
||||
if err := os.MkdirAll(targetDir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
binaryPath := filepath.Join(targetDir, pocketbaseBinaryName(runtime.GOOS))
|
||||
if err := downloadPocketbase(defaultPocketbaseVersion, runtime.GOOS, runtime.GOARCH, binaryPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ensureGitignoreEntries(filepath.Join(cwd, ".gitignore"), []string{"bin/pocketbase", "pb_data"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Initialized PocketBase project %q\n", serviceName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func initAction() func(*cli.Context) error {
|
||||
return func(c *cli.Context) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pbPath := filepath.Join(cwd, "pb.toml")
|
||||
if _, err := os.Stat(pbPath); err == nil {
|
||||
return fmt.Errorf("pb.toml already exists, aborting")
|
||||
} else if !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
serviceName := generateServiceName()
|
||||
|
||||
if err := writePBConfig(pbPath, serviceName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targetDir := filepath.Join(cwd, "bin")
|
||||
if err := os.MkdirAll(targetDir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
binaryPath := filepath.Join(targetDir, pocketbaseBinaryName(runtime.GOOS))
|
||||
if err := downloadPocketbase(defaultPocketbaseVersion, runtime.GOOS, runtime.GOARCH, binaryPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ensureGitignoreEntries(filepath.Join(cwd, ".gitignore"), []string{"bin/", "pb.toml"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Initialized PocketBase project %q\n", serviceName)
|
||||
return nil
|
||||
func runDev() error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
binaryPath := filepath.Join(cwd, "bin", pocketbaseBinaryName(runtime.GOOS))
|
||||
if _, err := os.Stat(binaryPath); err != nil {
|
||||
return fmt.Errorf("cannot find PocketBase binary at %s: %w", binaryPath, err)
|
||||
}
|
||||
|
||||
overrides, err := loadEnv(filepath.Join(cwd, ".env"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command(binaryPath, "serve")
|
||||
cmd.Args = append(cmd.Args, "--dir", filepath.Join(cwd, "pb_data"))
|
||||
cmd.Dir = cwd
|
||||
cmd.Env = mergeEnv(overrides)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
|
||||
fmt.Println("starting PocketBase development server")
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
const defaultPocketbaseVersion = "0.35.1"
|
||||
|
||||
Reference in New Issue
Block a user