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