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: loadRepo,
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}