commands: add "bridge rm"

Michael Muré created

Change summary

bridge/bridges.go            |  9 +++++++++
bridge/core/bridge.go        | 31 +++++++++++++++----------------
bridge/github/config.go      |  2 +-
cache/repo_cache.go          |  7 +++++++
commands/bridge_configure.go |  3 +--
commands/bridge_rm.go        | 33 +++++++++++++++++++++++++++++++++
doc/man/git-bug-bridge-rm.1  | 29 +++++++++++++++++++++++++++++
doc/man/git-bug-bridge.1     |  2 +-
doc/md/git-bug_bridge.md     |  1 +
doc/md/git-bug_bridge_rm.md  | 22 ++++++++++++++++++++++
misc/bash_completion/git-bug | 21 +++++++++++++++++++++
misc/zsh_completion/git-bug  |  8 ++++----
repository/git.go            | 15 ++++++++++++++-
repository/mock_repo.go      |  9 +++++++++
repository/repo.go           |  3 +++
15 files changed, 170 insertions(+), 25 deletions(-)

Detailed changes

bridge/bridges.go 🔗

@@ -3,6 +3,7 @@ package bridge
 import (
 	"github.com/MichaelMure/git-bug/bridge/core"
 	_ "github.com/MichaelMure/git-bug/bridge/github"
+	"github.com/MichaelMure/git-bug/cache"
 	"github.com/MichaelMure/git-bug/repository"
 )
 
@@ -11,6 +12,14 @@ func Targets() []string {
 	return core.Targets()
 }
 
+func NewBridge(repo *cache.RepoCache, target string, name string) (*core.Bridge, error) {
+	return core.NewBridge(repo, target, name)
+}
+
 func ConfiguredBridges(repo repository.RepoCommon) ([]string, error) {
 	return core.ConfiguredBridges(repo)
 }
+
+func RemoveBridges(repo repository.RepoCommon, fullName string) error {
+	return core.RemoveBridge(repo, fullName)
+}

bridge/core/bridge.go 🔗

@@ -62,14 +62,14 @@ func NewBridge(repo *cache.RepoCache, target string, name string) (*Bridge, erro
 }
 
 func ConfiguredBridges(repo repository.RepoCommon) ([]string, error) {
-	configs, err := repo.ReadConfigs("git-bug.")
+	configs, err := repo.ReadConfigs("git-bug.bridge.")
 	if err != nil {
 		return nil, errors.Wrap(err, "can't read configured bridges")
 	}
 
-	re, err := regexp.Compile(`git-bug.([^\.]+\.[^\.]+)`)
+	re, err := regexp.Compile(`git-bug.bridge.([^\.]+\.[^\.]+)`)
 	if err != nil {
-		return nil, err
+		panic(err)
 	}
 
 	set := make(map[string]interface{})
@@ -95,19 +95,18 @@ func ConfiguredBridges(repo repository.RepoCommon) ([]string, error) {
 	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")
+func RemoveBridge(repo repository.RepoCommon, fullName string) error {
+	re, err := regexp.Compile(`^[^\.]+\.[^\.]+$`)
+	if err != nil {
+		panic(err)
+	}
+
+	if !re.MatchString(fullName) {
+		return fmt.Errorf("bad bridge fullname: %s", fullName)
 	}
 
-	return fmt.Sprintf("%s.%s: %s", b.impl.Target(), b.Name, _type)
+	keyPrefix := fmt.Sprintf("git-bug.bridge.%s", fullName)
+	return repo.RmConfigs(keyPrefix)
 }
 
 func (b *Bridge) Configure() error {
@@ -123,7 +122,7 @@ func (b *Bridge) Configure() error {
 
 func (b *Bridge) storeConfig(conf Configuration) error {
 	for key, val := range conf {
-		storeKey := fmt.Sprintf("git-bug.%s.%s.%s", b.impl.Target(), b.Name, key)
+		storeKey := fmt.Sprintf("git-bug.bridge.%s.%s.%s", b.impl.Target(), b.Name, key)
 
 		err := b.repo.StoreConfig(storeKey, val)
 		if err != nil {
@@ -147,7 +146,7 @@ func (b Bridge) getConfig() (Configuration, error) {
 }
 
 func (b Bridge) loadConfig() (Configuration, error) {
-	keyPrefix := fmt.Sprintf("git-bug.%s.%s.", b.impl.Target(), b.Name)
+	keyPrefix := fmt.Sprintf("git-bug.bridge.%s.%s.", b.impl.Target(), b.Name)
 
 	pairs, err := b.repo.ReadConfigs(keyPrefix)
 	if err != nil {

bridge/github/config.go 🔗

@@ -214,7 +214,7 @@ func promptURL() (string, string, error) {
 func splitURL(url string) (string, string, error) {
 	re, err := regexp.Compile(`github\.com\/([^\/]*)\/([^\/]*)`)
 	if err != nil {
-		return "", "", err
+		panic(err)
 	}
 
 	res := re.FindStringSubmatch(url)

cache/repo_cache.go 🔗

@@ -75,14 +75,21 @@ func (c *RepoCache) GetUserEmail() (string, error) {
 	return c.repo.GetUserEmail()
 }
 
+// StoreConfig store a single key/value pair in the config of the repo
 func (c *RepoCache) StoreConfig(key string, value string) error {
 	return c.repo.StoreConfig(key, value)
 }
 
+// ReadConfigs read all key/value pair matching the key prefix
 func (c *RepoCache) ReadConfigs(keyPrefix string) (map[string]string, error) {
 	return c.repo.ReadConfigs(keyPrefix)
 }
 
+// RmConfigs remove all key/value pair matching the key prefix
+func (c *RepoCache) RmConfigs(keyPrefix string) error {
+	return c.repo.RmConfigs(keyPrefix)
+}
+
 func (c *RepoCache) lock() error {
 	lockPath := repoLockFilePath(c.repo)
 

commands/bridge_configure.go 🔗

@@ -7,7 +7,6 @@ import (
 	"strings"
 
 	"github.com/MichaelMure/git-bug/bridge"
-	"github.com/MichaelMure/git-bug/bridge/core"
 	"github.com/MichaelMure/git-bug/cache"
 	"github.com/spf13/cobra"
 )
@@ -29,7 +28,7 @@ func runBridgeConfigure(cmd *cobra.Command, args []string) error {
 		return err
 	}
 
-	b, err := core.NewBridge(backend, target, name)
+	b, err := bridge.NewBridge(backend, target, name)
 	if err != nil {
 		return err
 	}

commands/bridge_rm.go 🔗

@@ -0,0 +1,33 @@
+package commands
+
+import (
+	"github.com/MichaelMure/git-bug/bridge"
+	"github.com/MichaelMure/git-bug/cache"
+	"github.com/spf13/cobra"
+)
+
+func runBridgeRm(cmd *cobra.Command, args []string) error {
+	backend, err := cache.NewRepoCache(repo)
+	if err != nil {
+		return err
+	}
+	defer backend.Close()
+
+	err = bridge.RemoveBridges(backend, args[0])
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+var bridgeRmCmd = &cobra.Command{
+	Use:   "rm name <name>",
+	Short: "Delete a configured bridge",
+	RunE:  runBridgeRm,
+	Args:  cobra.ExactArgs(1),
+}
+
+func init() {
+	bridgeCmd.AddCommand(bridgeRmCmd)
+}

doc/man/git-bug-bridge-rm.1 🔗

@@ -0,0 +1,29 @@
+.TH "GIT-BUG" "1" "Sep 2018" "Generated from git-bug's source code" "" 
+.nh
+.ad l
+
+
+.SH NAME
+.PP
+git\-bug\-bridge\-rm \- Delete a configured bridge
+
+
+.SH SYNOPSIS
+.PP
+\fBgit\-bug bridge rm name <name> [flags]\fP
+
+
+.SH DESCRIPTION
+.PP
+Delete a configured bridge
+
+
+.SH OPTIONS
+.PP
+\fB\-h\fP, \fB\-\-help\fP[=false]
+    help for rm
+
+
+.SH SEE ALSO
+.PP
+\fBgit\-bug\-bridge(1)\fP

doc/man/git-bug-bridge.1 🔗

@@ -26,4 +26,4 @@ Configure and use bridges to other bug trackers
 
 .SH SEE ALSO
 .PP
-\fBgit\-bug(1)\fP, \fBgit\-bug\-bridge\-configure(1)\fP
+\fBgit\-bug(1)\fP, \fBgit\-bug\-bridge\-configure(1)\fP, \fBgit\-bug\-bridge\-rm(1)\fP

doc/md/git-bug_bridge.md 🔗

@@ -20,4 +20,5 @@ git-bug bridge [flags]
 
 * [git-bug](git-bug.md)	 - A bug tracker embedded in Git
 * [git-bug bridge configure](git-bug_bridge_configure.md)	 - Configure a new bridge
+* [git-bug bridge rm](git-bug_bridge_rm.md)	 - Delete a configured bridge
 

doc/md/git-bug_bridge_rm.md 🔗

@@ -0,0 +1,22 @@
+## git-bug bridge rm
+
+Delete a configured bridge
+
+### Synopsis
+
+Delete a configured bridge
+
+```
+git-bug bridge rm name <name> [flags]
+```
+
+### Options
+
+```
+  -h, --help   help for rm
+```
+
+### SEE ALSO
+
+* [git-bug bridge](git-bug_bridge.md)	 - Configure and use bridges to other bug trackers
+

misc/bash_completion/git-bug 🔗

@@ -297,6 +297,26 @@ _git-bug_bridge_configure()
     noun_aliases=()
 }
 
+_git-bug_bridge_rm()
+{
+    last_command="git-bug_bridge_rm"
+
+    command_aliases=()
+
+    commands=()
+
+    flags=()
+    two_word_flags=()
+    local_nonpersistent_flags=()
+    flags_with_completion=()
+    flags_completion=()
+
+
+    must_have_one_flag=()
+    must_have_one_noun=()
+    noun_aliases=()
+}
+
 _git-bug_bridge()
 {
     last_command="git-bug_bridge"
@@ -305,6 +325,7 @@ _git-bug_bridge()
 
     commands=()
     commands+=("configure")
+    commands+=("rm")
 
     flags=()
     two_word_flags=()

misc/zsh_completion/git-bug 🔗

@@ -17,8 +17,11 @@ case $state in
   ;;
   level2)
     case $words[2] in
+      title)
+        _arguments '2: :(edit)'
+      ;;
       bridge)
-        _arguments '2: :(configure)'
+        _arguments '2: :(configure rm)'
       ;;
       comment)
         _arguments '2: :(add)'
@@ -29,9 +32,6 @@ case $state in
       status)
         _arguments '2: :(close open)'
       ;;
-      title)
-        _arguments '2: :(edit)'
-      ;;
       *)
         _arguments '*: :_files'
       ;;

repository/git.go 🔗

@@ -170,8 +170,14 @@ func (repo *GitRepo) StoreConfig(key string, value string) error {
 func (repo *GitRepo) ReadConfigs(keyPrefix string) (map[string]string, error) {
 	stdout, err := repo.runGitCommand("config", "--get-regexp", keyPrefix)
 
+	//   / \
+	//  / ! \
+	// -------
+	//
+	// There can be a legitimate error here, but I see no portable way to
+	// distinguish them from the git error that say "no matching value exist"
 	if err != nil {
-		return nil, err
+		return nil, nil
 	}
 
 	lines := strings.Split(stdout, "\n")
@@ -194,6 +200,13 @@ func (repo *GitRepo) ReadConfigs(keyPrefix string) (map[string]string, error) {
 	return result, nil
 }
 
+// RmConfigs remove all key/value pair matching the key prefix
+func (repo *GitRepo) RmConfigs(keyPrefix string) error {
+	_, err := repo.runGitCommand("config", "--remove-section", keyPrefix)
+
+	return err
+}
+
 // FetchRefs fetch git refs from a remote
 func (repo *GitRepo) FetchRefs(remote, refSpec string) (string, error) {
 	stdout, err := repo.runGitCommand("fetch", remote, refSpec)

repository/mock_repo.go 🔗

@@ -73,6 +73,15 @@ func (r *mockRepoForTest) ReadConfigs(keyPrefix string) (map[string]string, erro
 	return result, nil
 }
 
+func (r *mockRepoForTest) RmConfigs(keyPrefix string) error {
+	for key := range r.config {
+		if strings.HasPrefix(key, keyPrefix) {
+			delete(r.config, key)
+		}
+	}
+	return nil
+}
+
 // PushRefs push git refs to a remote
 func (r *mockRepoForTest) PushRefs(remote string, refSpec string) (string, error) {
 	return "", nil

repository/repo.go 🔗

@@ -28,6 +28,9 @@ type RepoCommon interface {
 
 	// ReadConfigs read all key/value pair matching the key prefix
 	ReadConfigs(keyPrefix string) (map[string]string, error)
+
+	// RmConfigs remove all key/value pair matching the key prefix
+	RmConfigs(keyPrefix string) error
 }
 
 // Repo represents a source code repository.