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 if result.Event != core.ImportEventNothing {
100 fmt.Println(result.String())
101 }
102
103 switch result.Event {
104 case core.ImportEventBug:
105 importedIssues++
106 case core.ImportEventIdentity:
107 importedIdentities++
108 }
109 }
110
111 fmt.Printf("imported %d issues and %d identities with %s bridge\n", importedIssues, importedIdentities, b.Name)
112
113 // send done signal
114 close(done)
115
116 return nil
117}
118
119func parseSince(since string) (time.Time, error) {
120 duration, err := time.ParseDuration(since)
121 if err == nil {
122 return time.Now().Add(-duration), nil
123 }
124
125 return dateparse.ParseLocal(since)
126}
127
128var bridgePullCmd = &cobra.Command{
129 Use: "pull [<name>]",
130 Short: "Pull updates.",
131 PreRunE: loadRepoEnsureUser,
132 RunE: runBridgePull,
133 Args: cobra.MaximumNArgs(1),
134}
135
136func init() {
137 bridgeCmd.AddCommand(bridgePullCmd)
138 bridgePullCmd.Flags().BoolVarP(&bridgePullNoResume, "no-resume", "n", false, "force importing all bugs")
139 bridgePullCmd.Flags().StringVarP(&bridgePullImportSince, "since", "s", "", "import only bugs updated after the given date (ex: \"200h\" or \"june 2 2019\")")
140}