1package commands
2
3import (
4 "context"
5 "fmt"
6 "os"
7 "sync"
8 "time"
9
10 "github.com/araddon/dateparse"
11 "github.com/pkg/errors"
12 "github.com/spf13/cobra"
13
14 "github.com/MichaelMure/git-bug/bridge"
15 "github.com/MichaelMure/git-bug/bridge/core"
16 "github.com/MichaelMure/git-bug/cache"
17 "github.com/MichaelMure/git-bug/util/interrupt"
18)
19
20var (
21 bridgePullImportSince string
22 bridgePullNoResume bool
23)
24
25func runBridgePull(cmd *cobra.Command, args []string) error {
26 if bridgePullNoResume && bridgePullImportSince != "" {
27 return fmt.Errorf("only one of --no-resume and --since flags should be used")
28 }
29
30 backend, err := cache.NewRepoCache(repo)
31 if err != nil {
32 return err
33 }
34 defer backend.Close()
35 interrupt.RegisterCleaner(backend.Close)
36
37 var b *core.Bridge
38
39 if len(args) == 0 {
40 b, err = bridge.DefaultBridge(backend)
41 } else {
42 b, err = bridge.LoadBridge(backend, args[0])
43 }
44
45 if err != nil {
46 return err
47 }
48
49 parentCtx := context.Background()
50 ctx, cancel := context.WithCancel(parentCtx)
51 defer cancel()
52
53 // buffered channel to avoid send block at the end
54 done := make(chan struct{}, 1)
55
56 var mu sync.Mutex
57 interruptCount := 0
58 interrupt.RegisterCleaner(func() error {
59 mu.Lock()
60 if interruptCount > 0 {
61 fmt.Println("Received another interrupt before graceful stop, terminating...")
62 os.Exit(0)
63 }
64
65 interruptCount++
66 mu.Unlock()
67
68 fmt.Println("Received interrupt signal, stopping the import...\n(Hit ctrl-c again to kill the process.)")
69
70 // send signal to stop the importer
71 cancel()
72
73 // block until importer gracefully shutdown
74 <-done
75 return nil
76 })
77
78 var events <-chan core.ImportResult
79 switch {
80 case bridgePullNoResume:
81 events, err = b.ImportAllSince(ctx, time.Time{})
82 case bridgePullImportSince != "":
83 since, err2 := parseSince(bridgePullImportSince)
84 if err2 != nil {
85 return errors.Wrap(err2, "import time parsing")
86 }
87 events, err = b.ImportAllSince(ctx, since)
88 default:
89 events, err = b.ImportAll(ctx)
90 }
91
92 if err != nil {
93 return err
94 }
95
96 importedIssues := 0
97 importedIdentities := 0
98 for result := range events {
99 switch result.Event {
100 case core.ImportEventNothing:
101 // filtered
102
103 case core.ImportEventBug:
104 importedIssues++
105 fmt.Println(result.String())
106
107 case core.ImportEventIdentity:
108 importedIdentities++
109 fmt.Println(result.String())
110
111 case core.ImportEventError:
112 if result.Err != context.Canceled {
113 fmt.Println(result.String())
114 }
115
116 default:
117 fmt.Println(result.String())
118 }
119 }
120
121 fmt.Printf("imported %d issues and %d identities with %s bridge\n", importedIssues, importedIdentities, b.Name)
122
123 // send done signal
124 close(done)
125
126 return nil
127}
128
129func parseSince(since string) (time.Time, error) {
130 duration, err := time.ParseDuration(since)
131 if err == nil {
132 return time.Now().Add(-duration), nil
133 }
134
135 return dateparse.ParseLocal(since)
136}
137
138var bridgePullCmd = &cobra.Command{
139 Use: "pull [<name>]",
140 Short: "Pull updates.",
141 PreRunE: loadRepoEnsureUser,
142 RunE: runBridgePull,
143 Args: cobra.MaximumNArgs(1),
144}
145
146func init() {
147 bridgeCmd.AddCommand(bridgePullCmd)
148 bridgePullCmd.Flags().BoolVarP(&bridgePullNoResume, "no-resume", "n", false, "force importing all bugs")
149 bridgePullCmd.Flags().StringVarP(&bridgePullImportSince, "since", "s", "", "import only bugs updated after the given date (ex: \"200h\" or \"june 2 2019\")")
150}