-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 'refactor(utils): simplify Shell command construction' * refactor(utils): replace Shell with RunShell and RunOutput * refactor(git): simplify git command execution and output handling * refactor(utils): improve error handling in RunShell function * refactor(utils): simplify RunShell function by using RunOutput * refactor(utils): trim shell command result before logging * refactor(bootstrap): simplify boot.go and extract config logic * fix: barry 2025-01-04 22:08:28 * style: Reorder imports for better readability
- Loading branch information
Showing
13 changed files
with
541 additions
and
172 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,145 +1,61 @@ | ||
package bootstrap | ||
|
||
import ( | ||
"context" | ||
_ "embed" | ||
"fmt" | ||
"log/slog" | ||
"os" | ||
"sort" | ||
|
||
"github.com/adrg/xdg" | ||
_ "github.com/adrg/xdg" | ||
_ "github.com/charmbracelet/bubbletea" | ||
"github.com/charmbracelet/x/term" | ||
"github.com/pubgo/dix" | ||
"github.com/pubgo/dix/dix_internal" | ||
"github.com/pubgo/fastcommit/cmds/fastcommit" | ||
"github.com/pubgo/fastcommit/cmds/tagcmd" | ||
"github.com/pubgo/fastcommit/cmds/versioncmd" | ||
"github.com/pubgo/fastcommit/configs" | ||
"github.com/pubgo/fastcommit/utils" | ||
"github.com/pubgo/funk/assert" | ||
"github.com/pubgo/funk/config" | ||
"github.com/pubgo/funk/env" | ||
"github.com/pubgo/funk/pathutil" | ||
"github.com/pubgo/funk/recovery" | ||
"github.com/pubgo/funk/running" | ||
"github.com/pubgo/funk/version" | ||
"github.com/pubgo/funk/typex" | ||
"github.com/rs/zerolog" | ||
"github.com/sashabaranov/go-openai" | ||
_ "github.com/sashabaranov/go-openai" | ||
"github.com/urfave/cli/v3" | ||
"gopkg.in/yaml.v3" | ||
) | ||
|
||
var configPath = assert.Exit1(xdg.ConfigFile("fastcommit/config.yaml")) | ||
|
||
//go:embed default.yaml | ||
var defaultConfig []byte | ||
|
||
func Main() { | ||
defer recovery.Exit() | ||
|
||
var branchName = string(assert.Exit1(utils.ShellOutput("git", "rev-parse", "--abbrev-ref", "HEAD"))) | ||
|
||
slog.Info("config path", "path", configPath) | ||
if pathutil.IsNotExist(configPath) { | ||
assert.Must(os.WriteFile(configPath, defaultConfig, 0644)) | ||
} | ||
typex.DoBlock(func() { | ||
if pathutil.IsNotExist(configPath) { | ||
assert.Must(os.WriteFile(configPath, defaultConfig, 0644)) | ||
return | ||
} | ||
|
||
config.SetConfigPath(configPath) | ||
var cfg ConfigProvider | ||
config.LoadFromPath(&cfg, configPath) | ||
|
||
var defaultCfg ConfigProvider | ||
assert.Exit(yaml.Unmarshal(defaultConfig, &defaultCfg)) | ||
if cfg.Version == nil || cfg.Version.Name == "" || defaultCfg.Version.Name != cfg.Version.Name { | ||
assert.Exit(os.WriteFile(configPath, defaultConfig, 0644)) | ||
} | ||
}) | ||
|
||
config.SetConfigPath(configPath) | ||
dix_internal.SetLogLevel(zerolog.InfoLevel) | ||
|
||
var di = dix.New(dix.WithValuesNull()) | ||
di.Provide(versioncmd.New) | ||
di.Provide(tagcmd.New) | ||
di.Provide(config.Load[Config]) | ||
di.Provide(utils.NewOpenaiClient) | ||
|
||
di.Inject(func(cmd []*cli.Command) { | ||
app := &cli.Command{ | ||
Name: "fastcommit", | ||
Suggest: true, | ||
UseShortOptionHandling: true, | ||
ShellComplete: cli.DefaultAppComplete, | ||
Usage: "Intelligent generation of git commit message", | ||
Version: version.Version(), | ||
Flags: []cli.Flag{ | ||
&cli.StringFlag{ | ||
Name: "config", | ||
Aliases: []string{"c"}, | ||
Usage: "config file path", | ||
Value: config.GetConfigPath(), | ||
Persistent: true, | ||
Sources: cli.EnvVars(env.Key("fast_commit_config")), | ||
Action: func(ctx context.Context, command *cli.Command, s string) error { | ||
config.SetConfigPath(s) | ||
return nil | ||
}, | ||
}, | ||
&cli.BoolFlag{ | ||
Name: "debug", | ||
Usage: "enable debug mode", | ||
Persistent: true, | ||
Value: running.IsDebug, | ||
Destination: &running.IsDebug, | ||
Sources: cli.EnvVars(env.Key("debug"), env.Key("enable_debug")), | ||
}, | ||
}, | ||
Commands: cmd, | ||
Action: func(ctx context.Context, command *cli.Command) error { | ||
if utils.IsHelp() { | ||
return cli.ShowAppHelp(command) | ||
} | ||
|
||
if !term.IsTerminal(os.Stdin.Fd()) { | ||
return nil | ||
} | ||
|
||
generatePrompt := utils.GeneratePrompt("en", 50, utils.ConventionalCommitType) | ||
|
||
repoPath := assert.Must1(utils.AssertGitRepo()) | ||
slog.Info("Git repository root", "path", repoPath) | ||
|
||
assert.Exit(utils.Shell("git", "add", "--update").Run()) | ||
|
||
diff := assert.Must1(utils.GetStagedDiff(nil)) | ||
|
||
client := dix.Inject(di, new(struct { | ||
*utils.OpenaiClient | ||
})) | ||
resp, err := client.Client.CreateChatCompletion( | ||
context.Background(), | ||
openai.ChatCompletionRequest{ | ||
Model: client.Cfg.Model, | ||
Messages: []openai.ChatCompletionMessage{ | ||
{ | ||
Role: openai.ChatMessageRoleSystem, | ||
Content: generatePrompt, | ||
}, | ||
{ | ||
Role: openai.ChatMessageRoleUser, | ||
Content: diff["diff"].(string), | ||
}, | ||
}, | ||
}, | ||
) | ||
|
||
if err != nil { | ||
fmt.Printf("ChatCompletion error: %v\n", err) | ||
} | ||
|
||
if len(resp.Choices) == 0 { | ||
return nil | ||
} | ||
|
||
msg := resp.Choices[0].Message.Content | ||
assert.Must(utils.Shell("git", "commit", "-m", fmt.Sprintf("'%s'", msg)).Run()) | ||
assert.Must(utils.Shell("git", "push", "origin", branchName).Run()) | ||
|
||
return nil | ||
}, | ||
di.Provide(func() *configs.Config { | ||
return &configs.Config{ | ||
BranchName: branchName, | ||
} | ||
|
||
sort.Sort(cli.FlagsByName(app.Flags)) | ||
assert.Must(app.Run(utils.Context(), os.Args)) | ||
}) | ||
di.Provide(tagcmd.New) | ||
di.Provide(config.Load[ConfigProvider]) | ||
di.Provide(utils.NewOpenaiClient) | ||
di.Provide(fastcommit.New) | ||
di.Inject(func(cmd *fastcommit.Command) { cmd.Run() }) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,21 @@ | ||
package bootstrap | ||
|
||
import "github.com/pubgo/fastcommit/utils" | ||
import ( | ||
_ "embed" | ||
|
||
type Config struct { | ||
"github.com/adrg/xdg" | ||
"github.com/pubgo/fastcommit/configs" | ||
"github.com/pubgo/fastcommit/utils" | ||
"github.com/pubgo/funk/assert" | ||
) | ||
|
||
type ConfigProvider struct { | ||
Version *configs.Version `yaml:"version"` | ||
OpenaiConfig *utils.OpenaiConfig `yaml:"openai"` | ||
} | ||
|
||
var configPath = assert.Exit1(xdg.ConfigFile("fastcommit/config.yaml")) | ||
var branchName = assert.Exit1(utils.RunOutput("git", "rev-parse", "--abbrev-ref", "HEAD")) | ||
|
||
//go:embed default.yaml | ||
var defaultConfig []byte |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,133 @@ | ||
package fastcommit | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log/slog" | ||
"os" | ||
"sort" | ||
|
||
tea "github.com/charmbracelet/bubbletea" | ||
"github.com/charmbracelet/x/term" | ||
"github.com/pubgo/dix" | ||
"github.com/pubgo/fastcommit/configs" | ||
"github.com/pubgo/fastcommit/utils" | ||
"github.com/pubgo/funk/assert" | ||
"github.com/pubgo/funk/env" | ||
"github.com/pubgo/funk/errors" | ||
"github.com/pubgo/funk/recovery" | ||
"github.com/pubgo/funk/running" | ||
"github.com/pubgo/funk/version" | ||
"github.com/sashabaranov/go-openai" | ||
"github.com/urfave/cli/v3" | ||
) | ||
|
||
type Params struct { | ||
Di *dix.Dix | ||
Cmd []*cli.Command | ||
Cfg *configs.Config | ||
OpenaiClient *utils.OpenaiClient | ||
} | ||
|
||
func New(params Params) *Command { | ||
app := &cli.Command{ | ||
Name: "fastcommit", | ||
Suggest: true, | ||
UseShortOptionHandling: true, | ||
ShellComplete: cli.DefaultAppComplete, | ||
Usage: "Intelligent generation of git commit message", | ||
Version: version.Version(), | ||
Flags: []cli.Flag{ | ||
&cli.BoolFlag{ | ||
Name: "debug", | ||
Usage: "enable debug mode", | ||
Persistent: true, | ||
Value: running.IsDebug, | ||
Destination: &running.IsDebug, | ||
Sources: cli.EnvVars(env.Key("debug"), env.Key("enable_debug")), | ||
}, | ||
}, | ||
Commands: params.Cmd, | ||
Action: func(ctx context.Context, command *cli.Command) error { | ||
var cfg = params.Cfg | ||
if utils.IsHelp() { | ||
return cli.ShowAppHelp(command) | ||
} | ||
|
||
if !term.IsTerminal(os.Stdin.Fd()) { | ||
return nil | ||
} | ||
|
||
generatePrompt := utils.GeneratePrompt("en", 50, utils.ConventionalCommitType) | ||
|
||
repoPath := assert.Must1(utils.AssertGitRepo()) | ||
slog.Info("Git repository root", "path", repoPath) | ||
|
||
assert.Exit(utils.RunShell("git", "add", "--update")) | ||
|
||
diff := assert.Must1(utils.GetStagedDiff(nil)) | ||
if diff == nil { | ||
return nil | ||
} | ||
|
||
if len(diff.Files) == 0 { | ||
return nil | ||
} | ||
|
||
slog.Info(utils.GetDetectedMessage(diff.Files)) | ||
|
||
resp, err := params.OpenaiClient.Client.CreateChatCompletion( | ||
context.Background(), | ||
openai.ChatCompletionRequest{ | ||
Model: params.OpenaiClient.Cfg.Model, | ||
Messages: []openai.ChatCompletionMessage{ | ||
{ | ||
Role: openai.ChatMessageRoleSystem, | ||
Content: generatePrompt, | ||
}, | ||
{ | ||
Role: openai.ChatMessageRoleUser, | ||
Content: diff.Diff, | ||
}, | ||
}, | ||
}, | ||
) | ||
|
||
if err != nil { | ||
slog.Error("failed to call openai", "err", err) | ||
return errors.WrapCaller(err) | ||
} | ||
|
||
if len(resp.Choices) == 0 { | ||
return nil | ||
} | ||
|
||
msg := resp.Choices[0].Message.Content | ||
fmt.Println(msg) | ||
var p1 = tea.NewProgram(InitialTextInputModel(msg)) | ||
mm := assert.Must1(p1.Run()).(model2) | ||
if mm.isExit() { | ||
return nil | ||
} | ||
|
||
msg = mm.Value() | ||
assert.Must(utils.RunShell("git", "commit", "-m", fmt.Sprintf("'%s'", msg))) | ||
assert.Must(utils.RunShell("git", "push", "origin", cfg.BranchName)) | ||
|
||
return nil | ||
}, | ||
} | ||
|
||
sort.Sort(cli.FlagsByName(app.Flags)) | ||
return &Command{cmd: app} | ||
} | ||
|
||
type Command struct { | ||
cmd *cli.Command | ||
} | ||
|
||
func (c *Command) Run() { | ||
defer recovery.Exit() | ||
sort.Sort(cli.FlagsByName(c.cmd.Flags)) | ||
assert.Exit(c.cmd.Run(utils.Context(), os.Args)) | ||
} |
Oops, something went wrong.