diff options
-rw-r--r-- | bridge/bridges.go | 15 | ||||
-rw-r--r-- | bridge/core/bridge.go | 165 | ||||
-rw-r--r-- | bridge/core/interfaces.go | 26 | ||||
-rw-r--r-- | bridge/github/config.go | 21 | ||||
-rw-r--r-- | bridge/github/github.go | 18 |
5 files changed, 167 insertions, 78 deletions
diff --git a/bridge/bridges.go b/bridge/bridges.go index c542903c..bfd51cb4 100644 --- a/bridge/bridges.go +++ b/bridge/bridges.go @@ -2,12 +2,15 @@ package bridge import ( "github.com/MichaelMure/git-bug/bridge/core" - "github.com/MichaelMure/git-bug/bridge/github" + _ "github.com/MichaelMure/git-bug/bridge/github" + "github.com/MichaelMure/git-bug/repository" ) -// Bridges return all known bridges -func Bridges() []*core.Bridge { - return []*core.Bridge{ - core.NewBridge(&github.Github{}), - } +// Targets return all known bridge implementation target +func Targets() []string { + return core.Targets() +} + +func ConfiguredBridges(repo repository.RepoCommon) ([]string, error) { + return core.ConfiguredBridges(repo) } diff --git a/bridge/core/bridge.go b/bridge/core/bridge.go index 3e3b935e..0c83e03e 100644 --- a/bridge/core/bridge.go +++ b/bridge/core/bridge.go @@ -2,7 +2,8 @@ package core import ( "fmt" - "os/exec" + "reflect" + "regexp" "strings" "github.com/MichaelMure/git-bug/cache" @@ -13,48 +14,130 @@ import ( var ErrImportNorSupported = errors.New("import is not supported") var ErrExportNorSupported = errors.New("export is not supported") +var bridgeImpl map[string]reflect.Type + // Bridge is a wrapper around a BridgeImpl that will bind low-level // implementation with utility code to provide high-level functions. type Bridge struct { + Name string + repo *cache.RepoCache impl BridgeImpl conf Configuration } -func NewBridge(impl BridgeImpl) *Bridge { - return &Bridge{ +// Register will register a new BridgeImpl +func Register(impl BridgeImpl) { + if bridgeImpl == nil { + bridgeImpl = make(map[string]reflect.Type) + } + bridgeImpl[impl.Target()] = reflect.TypeOf(impl) +} + +// Targets return all known bridge implementation target +func Targets() []string { + var result []string + + for key := range bridgeImpl { + result = append(result, key) + } + + return result +} + +func NewBridge(repo *cache.RepoCache, target string, name string) (*Bridge, error) { + implType, ok := bridgeImpl[target] + if !ok { + return nil, fmt.Errorf("unknown bridge target %v", target) + } + + impl := reflect.New(implType).Elem().Interface().(BridgeImpl) + + bridge := &Bridge{ + Name: name, + repo: repo, impl: impl, } + + return bridge, nil +} + +func ConfiguredBridges(repo repository.RepoCommon) ([]string, error) { + configs, err := repo.ReadConfigs("git-bug.") + if err != nil { + return nil, errors.Wrap(err, "can't read configured bridges") + } + + re, err := regexp.Compile(`git-bug.([^\.]+\.[^\.]+)`) + if err != nil { + return nil, err + } + + set := make(map[string]interface{}) + + for key := range configs { + res := re.FindStringSubmatch(key) + + if res == nil { + continue + } + + set[res[1]] = nil + } + + result := make([]string, len(set)) + + i := 0 + for key := range set { + result[i] = key + i++ + } + + return result, nil +} + +func (b *Bridge) String() string { + var _type string + if b.impl.Importer() != nil && b.impl.Exporter() != nil { + _type = "import/export" + } else if b.impl.Importer() != nil { + _type = "import" + } else if b.impl.Exporter() != nil { + _type = "export" + } else { + panic("bad bridge impl, neither import nor export") + } + + return fmt.Sprintf("%s.%s: %s", b.impl.Target(), b.Name, _type) } -func (b *Bridge) Configure(repo repository.RepoCommon) error { - conf, err := b.impl.Configure(repo) +func (b *Bridge) Configure() error { + conf, err := b.impl.Configure(b.repo) if err != nil { return err } - return b.storeConfig(repo, conf) + b.conf = conf + + return b.storeConfig(conf) } -func (b *Bridge) storeConfig(repo repository.RepoCommon, conf Configuration) error { +func (b *Bridge) storeConfig(conf Configuration) error { for key, val := range conf { - storeKey := fmt.Sprintf("git-bug.%s.%s", b.impl.Name(), key) - - cmd := exec.Command("git", "config", "--replace-all", storeKey, val) - cmd.Dir = repo.GetPath() + storeKey := fmt.Sprintf("git-bug.%s.%s.%s", b.impl.Target(), b.Name, key) - out, err := cmd.CombinedOutput() + err := b.repo.StoreConfig(storeKey, val) if err != nil { - return fmt.Errorf("error while storing bridge configuration: %s", out) + return errors.Wrap(err, "error while storing bridge configuration") } } return nil } -func (b Bridge) getConfig(repo repository.RepoCommon) (Configuration, error) { +func (b Bridge) getConfig() (Configuration, error) { var err error if b.conf == nil { - b.conf, err = b.loadConfig(repo) + b.conf, err = b.loadConfig() if err != nil { return nil, err } @@ -63,87 +146,75 @@ func (b Bridge) getConfig(repo repository.RepoCommon) (Configuration, error) { return b.conf, nil } -func (b Bridge) loadConfig(repo repository.RepoCommon) (Configuration, error) { - key := fmt.Sprintf("git-bug.%s", b.impl.Name()) - cmd := exec.Command("git", "config", "--get-regexp", key) - cmd.Dir = repo.GetPath() +func (b Bridge) loadConfig() (Configuration, error) { + keyPrefix := fmt.Sprintf("git-bug.%s.%s.", b.impl.Target(), b.Name) - out, err := cmd.CombinedOutput() + pairs, err := b.repo.ReadConfigs(keyPrefix) if err != nil { - return nil, fmt.Errorf("error while reading bridge configuration: %s", out) + return nil, errors.Wrap(err, "error while reading bridge configuration") } - lines := strings.Split(string(out), "\n") - - result := make(Configuration, len(lines)) - for _, line := range lines { - if strings.TrimSpace(line) == "" { - continue - } - - parts := strings.Fields(line) - if len(parts) != 2 { - return nil, fmt.Errorf("bad bridge configuration: %s", line) - } - - result[parts[0]] = parts[1] + result := make(Configuration, len(pairs)) + for key, value := range pairs { + key := strings.TrimPrefix(key, keyPrefix) + result[key] = value } return result, nil } -func (b Bridge) ImportAll(repo *cache.RepoCache) error { +func (b Bridge) ImportAll() error { importer := b.impl.Importer() if importer == nil { return ErrImportNorSupported } - conf, err := b.getConfig(repo) + conf, err := b.getConfig() if err != nil { return err } - return b.impl.Importer().ImportAll(repo, conf) + return b.impl.Importer().ImportAll(b.repo, conf) } -func (b Bridge) Import(repo *cache.RepoCache, id string) error { +func (b Bridge) Import(id string) error { importer := b.impl.Importer() if importer == nil { return ErrImportNorSupported } - conf, err := b.getConfig(repo) + conf, err := b.getConfig() if err != nil { return err } - return b.impl.Importer().Import(repo, conf, id) + return b.impl.Importer().Import(b.repo, conf, id) } -func (b Bridge) ExportAll(repo *cache.RepoCache) error { +func (b Bridge) ExportAll() error { exporter := b.impl.Exporter() if exporter == nil { return ErrExportNorSupported } - conf, err := b.getConfig(repo) + conf, err := b.getConfig() if err != nil { return err } - return b.impl.Exporter().ExportAll(repo, conf) + return b.impl.Exporter().ExportAll(b.repo, conf) } -func (b Bridge) Export(repo *cache.RepoCache, id string) error { +func (b Bridge) Export(id string) error { exporter := b.impl.Exporter() if exporter == nil { return ErrExportNorSupported } - conf, err := b.getConfig(repo) + conf, err := b.getConfig() if err != nil { return err } - return b.impl.Exporter().Export(repo, conf, id) + return b.impl.Exporter().Export(b.repo, conf, id) } diff --git a/bridge/core/interfaces.go b/bridge/core/interfaces.go index 5336e7a8..5ead3c8f 100644 --- a/bridge/core/interfaces.go +++ b/bridge/core/interfaces.go @@ -7,23 +7,27 @@ import ( type Configuration map[string]string -type Importer interface { - ImportAll(repo *cache.RepoCache, conf Configuration) error - Import(repo *cache.RepoCache, conf Configuration, id string) error -} - -type Exporter interface { - ExportAll(repo *cache.RepoCache, conf Configuration) error - Export(repo *cache.RepoCache, conf Configuration, id string) error -} - type BridgeImpl interface { - Name() string + // Target return the target of the bridge (e.g.: "github") + Target() string // Configure handle the user interaction and return a key/value configuration // for future use Configure(repo repository.RepoCommon) (Configuration, error) + // Importer return an Importer implementation if the import is supported Importer() Importer + + // Exporter return an Exporter implementation if the export is supported Exporter() Exporter } + +type Importer interface { + ImportAll(repo *cache.RepoCache, conf Configuration) error + Import(repo *cache.RepoCache, conf Configuration, id string) error +} + +type Exporter interface { + ExportAll(repo *cache.RepoCache, conf Configuration) error + Export(repo *cache.RepoCache, conf Configuration, id string) error +} diff --git a/bridge/github/config.go b/bridge/github/config.go index 3b12d3f9..385630a7 100644 --- a/bridge/github/config.go +++ b/bridge/github/config.go @@ -20,7 +20,7 @@ import ( "golang.org/x/crypto/ssh/terminal" ) -const githubV3Url = "https://api.Github.com" +const githubV3Url = "https://api.github.com" const keyUser = "user" const keyProject = "project" const keyToken = "token" @@ -28,6 +28,7 @@ const keyToken = "token" func (*Github) Configure(repo repository.RepoCommon) (core.Configuration, error) { conf := make(core.Configuration) + fmt.Println() fmt.Println("git-bug will generate an access token in your Github profile.") // fmt.Println("The token will have the \"repo\" permission, giving it read/write access to your repositories and issues. There is no narrower scope available, sorry :-|") fmt.Println() @@ -40,22 +41,16 @@ func (*Github) Configure(repo repository.RepoCommon) (core.Configuration, error) conf[keyUser] = projectUser conf[keyProject] = projectName - fmt.Println() - username, err := promptUsername() if err != nil { return nil, err } - fmt.Println() - password, err := promptPassword() if err != nil { return nil, err } - fmt.Println() - // Attempt to authenticate and create a token note := fmt.Sprintf("git-bug - %s/%s", projectUser, projectName) @@ -168,7 +163,7 @@ func randomFingerprint() string { func promptUsername() (string, error) { for { - fmt.Println("username:") + fmt.Print("username: ") line, err := bufio.NewReader(os.Stdin).ReadString('\n') if err != nil { @@ -191,7 +186,7 @@ func promptUsername() (string, error) { func promptURL() (string, string, error) { for { - fmt.Println("Github project URL:") + fmt.Print("Github project URL: ") line, err := bufio.NewReader(os.Stdin).ReadString('\n') if err != nil { @@ -249,9 +244,13 @@ func validateUsername(username string) (bool, error) { func promptPassword() (string, error) { for { - fmt.Println("password:") + fmt.Print("password: ") bytePassword, err := terminal.ReadPassword(int(syscall.Stdin)) + // new line for coherent formatting, ReadPassword clip the normal new line + // entered by the user + fmt.Println() + if err != nil { return "", err } @@ -266,7 +265,7 @@ func promptPassword() (string, error) { func prompt2FA() (string, error) { for { - fmt.Println("two-factor authentication code:") + fmt.Print("two-factor authentication code: ") byte2fa, err := terminal.ReadPassword(int(syscall.Stdin)) if err != nil { diff --git a/bridge/github/github.go b/bridge/github/github.go index 45954e23..4f7a3b94 100644 --- a/bridge/github/github.go +++ b/bridge/github/github.go @@ -1,13 +1,19 @@ package github import ( + "fmt" + "github.com/MichaelMure/git-bug/bridge/core" "github.com/MichaelMure/git-bug/cache" ) +func init() { + core.Register(&Github{}) +} + type Github struct{} -func (*Github) Name() string { +func (*Github) Target() string { return "github" } @@ -22,9 +28,15 @@ func (*Github) Exporter() core.Exporter { type githubImporter struct{} func (*githubImporter) ImportAll(repo *cache.RepoCache, conf core.Configuration) error { - panic("implement me") + fmt.Println(conf) + fmt.Println("IMPORT ALL") + + return nil } func (*githubImporter) Import(repo *cache.RepoCache, conf core.Configuration, id string) error { - panic("implement me") + fmt.Println(conf) + fmt.Println("IMPORT") + + return nil } |