aboutsummaryrefslogtreecommitdiffstats
path: root/bridge
diff options
context:
space:
mode:
authorMichael Muré <batolettre@gmail.com>2018-09-24 15:25:15 +0200
committerMichael Muré <batolettre@gmail.com>2018-09-24 15:25:15 +0200
commit5e8fb7ec5058b816ccc9622f52dbf1777254656c (patch)
tree76e27dff4c5128785e2e28e1544efff6fe398046 /bridge
parent666586c5b92b64a64aea22927cfb3754861623a1 (diff)
downloadgit-bug-5e8fb7ec5058b816ccc9622f52dbf1777254656c.tar.gz
bridge: big refactor and cleanup
Diffstat (limited to 'bridge')
-rw-r--r--bridge/bridges.go15
-rw-r--r--bridge/core/bridge.go165
-rw-r--r--bridge/core/interfaces.go26
-rw-r--r--bridge/github/config.go21
-rw-r--r--bridge/github/github.go18
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
}