aboutsummaryrefslogtreecommitdiffstats
path: root/commands/execenv
diff options
context:
space:
mode:
Diffstat (limited to 'commands/execenv')
-rw-r--r--commands/execenv/env.go191
-rw-r--r--commands/execenv/env_testing.go48
2 files changed, 239 insertions, 0 deletions
diff --git a/commands/execenv/env.go b/commands/execenv/env.go
new file mode 100644
index 00000000..a63f835a
--- /dev/null
+++ b/commands/execenv/env.go
@@ -0,0 +1,191 @@
+package execenv
+
+import (
+ "fmt"
+ "io"
+ "os"
+
+ "github.com/spf13/cobra"
+
+ "github.com/MichaelMure/git-bug/cache"
+ "github.com/MichaelMure/git-bug/entities/bug"
+ "github.com/MichaelMure/git-bug/entities/identity"
+ "github.com/MichaelMure/git-bug/repository"
+ "github.com/MichaelMure/git-bug/util/interrupt"
+)
+
+const RootCommandName = "git-bug"
+
+const gitBugNamespace = "git-bug"
+
+// Env is the environment of a command
+type Env struct {
+ Repo repository.ClockedRepo
+ Backend *cache.RepoCache
+ Out Out
+ Err Out
+}
+
+func NewEnv() *Env {
+ return &Env{
+ Repo: nil,
+ Out: out{Writer: os.Stdout},
+ Err: out{Writer: os.Stderr},
+ }
+}
+
+type Out interface {
+ io.Writer
+ Printf(format string, a ...interface{})
+ Print(a ...interface{})
+ Println(a ...interface{})
+
+ // String returns what have been written in the output before, as a string.
+ // This only works in test scenario.
+ String() string
+ // Bytes returns what have been written in the output before, as []byte.
+ // This only works in test scenario.
+ Bytes() []byte
+ // Reset clear what has been recorded as written in the output before.
+ // This only works in test scenario.
+ Reset()
+}
+
+type out struct {
+ io.Writer
+}
+
+func (o out) Printf(format string, a ...interface{}) {
+ _, _ = fmt.Fprintf(o, format, a...)
+}
+
+func (o out) Print(a ...interface{}) {
+ _, _ = fmt.Fprint(o, a...)
+}
+
+func (o out) Println(a ...interface{}) {
+ _, _ = fmt.Fprintln(o, a...)
+}
+
+func (o out) String() string {
+ panic("only work with a test env")
+}
+
+func (o out) Bytes() []byte {
+ panic("only work with a test env")
+}
+
+func (o out) Reset() {
+ panic("only work with a test env")
+}
+
+// LoadRepo is a pre-run function that load the repository for use in a command
+func LoadRepo(env *Env) func(*cobra.Command, []string) error {
+ return func(cmd *cobra.Command, args []string) error {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return fmt.Errorf("unable to get the current working directory: %q", err)
+ }
+
+ env.Repo, err = repository.OpenGoGitRepo(cwd, gitBugNamespace, []repository.ClockLoader{bug.ClockLoader})
+ if err == repository.ErrNotARepo {
+ return fmt.Errorf("%s must be run from within a git Repo", RootCommandName)
+ }
+
+ if err != nil {
+ return err
+ }
+
+ return nil
+ }
+}
+
+// LoadRepoEnsureUser is the same as LoadRepo, but also ensure that the user has configured
+// an identity. Use this pre-run function when an error after using the configured user won't
+// do.
+func LoadRepoEnsureUser(env *Env) func(*cobra.Command, []string) error {
+ return func(cmd *cobra.Command, args []string) error {
+ err := LoadRepo(env)(cmd, args)
+ if err != nil {
+ return err
+ }
+
+ _, err = identity.GetUserIdentity(env.Repo)
+ if err != nil {
+ return err
+ }
+
+ return nil
+ }
+}
+
+// LoadBackend is a pre-run function that load the repository and the Backend for use in a command
+// When using this function you also need to use CloseBackend as a post-run
+func LoadBackend(env *Env) func(*cobra.Command, []string) error {
+ return func(cmd *cobra.Command, args []string) error {
+ err := LoadRepo(env)(cmd, args)
+ if err != nil {
+ return err
+ }
+
+ env.Backend, err = cache.NewRepoCache(env.Repo)
+ if err != nil {
+ return err
+ }
+
+ cleaner := func(env *Env) interrupt.CleanerFunc {
+ return func() error {
+ if env.Backend != nil {
+ err := env.Backend.Close()
+ env.Backend = nil
+ return err
+ }
+ return nil
+ }
+ }
+
+ // Cleanup properly on interrupt
+ interrupt.RegisterCleaner(cleaner(env))
+ return nil
+ }
+}
+
+// LoadBackendEnsureUser is the same as LoadBackend, but also ensure that the user has configured
+// an identity. Use this pre-run function when an error after using the configured user won't
+// do.
+func LoadBackendEnsureUser(env *Env) func(*cobra.Command, []string) error {
+ return func(cmd *cobra.Command, args []string) error {
+ err := LoadBackend(env)(cmd, args)
+ if err != nil {
+ return err
+ }
+
+ _, err = identity.GetUserIdentity(env.Repo)
+ if err != nil {
+ return err
+ }
+
+ return nil
+ }
+}
+
+// CloseBackend is a wrapper for a RunE function that will close the Backend properly
+// if it has been opened.
+// This wrapper style is necessary because a Cobra PostE function does not run if RunE return an error.
+func CloseBackend(env *Env, runE func(cmd *cobra.Command, args []string) error) func(*cobra.Command, []string) error {
+ return func(cmd *cobra.Command, args []string) error {
+ errRun := runE(cmd, args)
+
+ if env.Backend == nil {
+ return nil
+ }
+ err := env.Backend.Close()
+ env.Backend = nil
+
+ // prioritize the RunE error
+ if errRun != nil {
+ return errRun
+ }
+ return err
+ }
+}
diff --git a/commands/execenv/env_testing.go b/commands/execenv/env_testing.go
new file mode 100644
index 00000000..7d9fbd60
--- /dev/null
+++ b/commands/execenv/env_testing.go
@@ -0,0 +1,48 @@
+package execenv
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/MichaelMure/git-bug/cache"
+ "github.com/MichaelMure/git-bug/repository"
+ "github.com/stretchr/testify/require"
+)
+
+type TestOut struct {
+ *bytes.Buffer
+}
+
+func (te *TestOut) Printf(format string, a ...interface{}) {
+ _, _ = fmt.Fprintf(te.Buffer, format, a...)
+}
+
+func (te *TestOut) Print(a ...interface{}) {
+ _, _ = fmt.Fprint(te.Buffer, a...)
+}
+
+func (te *TestOut) Println(a ...interface{}) {
+ _, _ = fmt.Fprintln(te.Buffer, a...)
+}
+
+func NewTestEnv(t *testing.T) *Env {
+ t.Helper()
+
+ repo := repository.CreateGoGitTestRepo(t, false)
+
+ buf := new(bytes.Buffer)
+
+ backend, err := cache.NewRepoCache(repo)
+ require.NoError(t, err)
+ t.Cleanup(func() {
+ backend.Close()
+ })
+
+ return &Env{
+ Repo: repo,
+ Backend: backend,
+ Out: &TestOut{buf},
+ Err: &TestOut{buf},
+ }
+}