loading.go

  1package execenv
  2
  3import (
  4	"fmt"
  5	"os"
  6	"path/filepath"
  7
  8	"github.com/spf13/cobra"
  9	"github.com/vbauerster/mpb/v8"
 10	"github.com/vbauerster/mpb/v8/decor"
 11
 12	"github.com/git-bug/git-bug/cache"
 13	"github.com/git-bug/git-bug/entities/identity"
 14	"github.com/git-bug/git-bug/repository"
 15	"github.com/git-bug/git-bug/util/interrupt"
 16)
 17
 18// LoadRepo is a pre-run function that load the repository for use in a command
 19func LoadRepo(env *Env) func(*cobra.Command, []string) error {
 20	return func(cmd *cobra.Command, args []string) error {
 21		repoPath, err := getRepoPath(env)
 22		if err != nil {
 23			return err
 24		}
 25
 26		// Note: we are not loading clocks here because we assume that LoadRepo is only used
 27		//  when we don't manipulate entities, or as a child call of LoadBackend which will
 28		//  read all clocks anyway.
 29		env.Repo, err = repository.OpenGoGitRepo(repoPath, gitBugNamespace, nil)
 30		if err == repository.ErrNotARepo {
 31			return fmt.Errorf("%s must be run from within a git Repo", RootCommandName)
 32		}
 33		if err != nil {
 34			return err
 35		}
 36
 37		return nil
 38	}
 39}
 40
 41// LoadRepoEnsureUser is the same as LoadRepo, but also ensure that the user has configured
 42// an identity. Use this pre-run function when an error after using the configured user won't
 43// do.
 44func LoadRepoEnsureUser(env *Env) func(*cobra.Command, []string) error {
 45	return func(cmd *cobra.Command, args []string) error {
 46		err := LoadRepo(env)(cmd, args)
 47		if err != nil {
 48			return err
 49		}
 50
 51		_, err = identity.GetUserIdentity(env.Repo)
 52		if err != nil {
 53			return err
 54		}
 55
 56		return nil
 57	}
 58}
 59
 60// LoadBackend is a pre-run function that load the repository and the Backend for use in a command
 61// When using this function you also need to use CloseBackend as a post-run
 62func LoadBackend(env *Env) func(*cobra.Command, []string) error {
 63	return func(cmd *cobra.Command, args []string) error {
 64		err := LoadRepo(env)(cmd, args)
 65		if err != nil {
 66			return err
 67		}
 68
 69		var events chan cache.BuildEvent
 70		env.Backend, events = cache.NewRepoCache(env.Repo)
 71
 72		err = CacheBuildProgressBar(env, events)
 73		if err != nil {
 74			return err
 75		}
 76
 77		cleaner := func(env *Env) interrupt.CleanerFunc {
 78			return func() error {
 79				if env.Backend != nil {
 80					err := env.Backend.Close()
 81					env.Backend = nil
 82					return err
 83				}
 84				return nil
 85			}
 86		}
 87
 88		// Cleanup properly on interrupt
 89		interrupt.RegisterCleaner(cleaner(env))
 90		return nil
 91	}
 92}
 93
 94// LoadBackendEnsureUser is the same as LoadBackend, but also ensure that the user has configured
 95// an identity. Use this pre-run function when an error after using the configured user won't
 96// do.
 97func LoadBackendEnsureUser(env *Env) func(*cobra.Command, []string) error {
 98	return func(cmd *cobra.Command, args []string) error {
 99		err := LoadBackend(env)(cmd, args)
100		if err != nil {
101			return err
102		}
103
104		_, err = identity.GetUserIdentity(env.Repo)
105		if err != nil {
106			return err
107		}
108
109		return nil
110	}
111}
112
113// CloseBackend is a wrapper for a RunE function that will close the Backend properly
114// if it has been opened.
115// This wrapper style is necessary because a Cobra PostE function does not run if RunE return an error.
116func CloseBackend(env *Env, runE func(cmd *cobra.Command, args []string) error) func(*cobra.Command, []string) error {
117	return func(cmd *cobra.Command, args []string) error {
118		errRun := runE(cmd, args)
119
120		if env.Backend == nil {
121			return nil
122		}
123		err := env.Backend.Close()
124		env.Backend = nil
125
126		// prioritize the RunE error
127		if errRun != nil {
128			return errRun
129		}
130		return err
131	}
132}
133
134func CacheBuildProgressBar(env *Env, events chan cache.BuildEvent) error {
135	var progress *mpb.Progress
136	var bars = make(map[string]*mpb.Bar)
137
138	for event := range events {
139		if event.Err != nil {
140			return event.Err
141		}
142
143		if progress == nil {
144			progress = mpb.New(mpb.WithOutput(env.Err.Raw()))
145		}
146
147		switch event.Event {
148		case cache.BuildEventCacheIsBuilt:
149			env.Err.Println("Building cache... ")
150		case cache.BuildEventStarted:
151			bars[event.Typename] = progress.AddBar(-1,
152				mpb.BarRemoveOnComplete(),
153				mpb.PrependDecorators(
154					decor.Name(event.Typename, decor.WCSyncSpace),
155					decor.CountersNoUnit("%d / %d", decor.WCSyncSpace),
156				),
157				mpb.AppendDecorators(decor.Percentage(decor.WCSyncSpace)),
158			)
159		case cache.BuildEventProgress:
160			bars[event.Typename].SetCurrent(event.Progress)
161			bars[event.Typename].SetTotal(event.Total, event.Progress == event.Total)
162		case cache.BuildEventFinished:
163			if bar := bars[event.Typename]; !bar.Completed() {
164				bar.SetTotal(0, true)
165			}
166		}
167	}
168
169	if progress != nil {
170		progress.Wait()
171	}
172
173	return nil
174}
175
176func getRepoPath(env *Env) (string, error) {
177	if len(env.RepoPath) > 0 {
178		return filepath.Join(env.RepoPath...), nil
179	}
180
181	cwd, err := os.Getwd()
182	if err != nil {
183		return "", fmt.Errorf("unable to get the current working directory: %q", err)
184	}
185	return cwd, nil
186}