1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2024-12-12 11:15:00 +02:00
lazygit/pkg/app/daemon/daemon.go
2023-04-29 07:28:33 +02:00

190 lines
5.3 KiB
Go

package daemon
import (
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
"github.com/fsmiamoto/git-todo-parser/todo"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/env"
"github.com/jesseduffield/lazygit/pkg/utils"
)
// Sometimes lazygit will be invoked in daemon mode from a parent lazygit process.
// We do this when git lets us supply a program to run within a git command.
// For example, if we want to ensure that a git command doesn't hang due to
// waiting for an editor to save a commit message, we can tell git to invoke lazygit
// as the editor via 'GIT_EDITOR=lazygit', and use the env var
// 'LAZYGIT_DAEMON_KIND=EXIT_IMMEDIATELY' to specify that we want to run lazygit
// as a daemon which simply exits immediately. Any additional arguments we want
// to pass to a daemon can be done via other env vars.
type DaemonKind string
const (
InteractiveRebase DaemonKind = "INTERACTIVE_REBASE"
ExitImmediately DaemonKind = "EXIT_IMMEDIATELY"
)
const (
DaemonKindEnvKey string = "LAZYGIT_DAEMON_KIND"
RebaseTODOEnvKey string = "LAZYGIT_REBASE_TODO"
// The `PrependLinesEnvKey` env variable is set to `true` to tell our daemon
// to prepend the content of `RebaseTODOEnvKey` to the default `git-rebase-todo`
// file instead of using it as a replacement.
PrependLinesEnvKey string = "LAZYGIT_PREPEND_LINES"
// If this is set, it tells lazygit to read the original todo file, and
// change the action for one or more entries in it. The value of the variable
// will have one or more lines of the form "Sha1:newAction", e.g.
// a02b54e1b7e7e8dd8bc1958c11ef4ee4df459ea4:edit
// The existing action of the todo to be changed is expected to be "pick".
//
// If this is used, the value of RebaseTODOEnvKey must be empty.
ChangeTodoActionEnvKey string = "LAZYGIT_CHANGE_TODO_ACTION"
// Can be set to the sha of a "pick" todo that will be moved down by one.
MoveTodoDownEnvKey string = "LAZYGIT_MOVE_COMMIT_DOWN"
// Can be set to the sha of a "pick" todo that will be moved up by one.
MoveTodoUpEnvKey string = "LAZYGIT_MOVE_COMMIT_UP"
)
type Daemon interface {
Run() error
}
var logFile io.StringWriter
func Handle(common *common.Common) {
logFile, _ = os.Create("/tmp/daemon-log.txt")
_, _ = logFile.WriteString("Hello Daemon\n")
d := getDaemon(common)
if d == nil {
return
}
if err := d.Run(); err != nil {
log.Fatal(err)
}
os.Exit(0)
}
func InDaemonMode() bool {
return getDaemonKind() != ""
}
func getDaemon(common *common.Common) Daemon {
switch getDaemonKind() {
case InteractiveRebase:
return &rebaseDaemon{c: common}
case ExitImmediately:
return &exitImmediatelyDaemon{c: common}
}
return nil
}
func getDaemonKind() DaemonKind {
return DaemonKind(os.Getenv(DaemonKindEnvKey))
}
type rebaseDaemon struct {
c *common.Common
}
func (self *rebaseDaemon) Run() error {
self.c.Log.Info("Lazygit invoked as interactive rebase demon")
self.c.Log.Info("args: ", os.Args)
path := os.Args[1]
if strings.HasSuffix(path, "git-rebase-todo") {
return self.writeTodoFile(path)
} else if strings.HasSuffix(path, filepath.Join(gitDir(), "COMMIT_EDITMSG")) { // TODO: test
// if we are rebasing and squashing, we'll see a COMMIT_EDITMSG
// but in this case we don't need to edit it, so we'll just return
} else {
self.c.Log.Info("Lazygit demon did not match on any use cases")
}
return nil
}
func (self *rebaseDaemon) writeTodoFile(path string) error {
if changeTodoActionEnvValue := os.Getenv(ChangeTodoActionEnvKey); changeTodoActionEnvValue != "" {
return self.changeTodoAction(path, changeTodoActionEnvValue)
} else if shaToMoveDown := os.Getenv(MoveTodoDownEnvKey); shaToMoveDown != "" {
_, _ = logFile.WriteString(fmt.Sprintf("Moving commit down: %s\n", shaToMoveDown))
return utils.MoveTodoDown(path, shaToMoveDown, todo.Pick)
} else if shaToMoveUp := os.Getenv(MoveTodoUpEnvKey); shaToMoveUp != "" {
_, _ = logFile.WriteString(fmt.Sprintf("Moving commit up: %s\n", shaToMoveUp))
return utils.MoveTodoUp(path, shaToMoveUp, todo.Pick)
} else {
todoContent := []byte(os.Getenv(RebaseTODOEnvKey))
prependLines := os.Getenv(PrependLinesEnvKey) != ""
if prependLines {
existingContent, err := os.ReadFile(path)
if err != nil {
return err
}
todoContent = append(todoContent, existingContent...)
}
return os.WriteFile(path, todoContent, 0o644)
}
}
func (self *rebaseDaemon) changeTodoAction(path string, changeTodoActionEnvValue string) error {
lines := strings.Split(changeTodoActionEnvValue, "\n")
for _, line := range lines {
fields := strings.Split(line, ":")
if len(fields) != 2 {
return fmt.Errorf("Unexpected value for %s: %s", ChangeTodoActionEnvKey, changeTodoActionEnvValue)
}
sha, newAction := fields[0], self.actionFromString(fields[1])
if int(newAction) == 0 {
return fmt.Errorf("Unknown action in %s", changeTodoActionEnvValue)
}
if err := utils.EditRebaseTodo(path, sha, todo.Pick, newAction); err != nil {
return err
}
}
return nil
}
func (self *rebaseDaemon) actionFromString(actionString string) todo.TodoCommand {
for t := todo.Pick; t < todo.Comment; t++ {
if t.String() == actionString {
return t
}
}
return 0
}
func gitDir() string {
dir := env.GetGitDirEnv()
if dir == "" {
return ".git"
}
return dir
}
type exitImmediatelyDaemon struct {
c *common.Common
}
func (self *exitImmediatelyDaemon) Run() error {
return nil
}