bridge: various cleanups

Michael Muré created

Change summary

bridge/bridges.go                  | 12 +++-
bridge/core/token.go               | 75 +++++++++++----------------
bridge/github/github.go            |  4 -
bridge/gitlab/gitlab.go            |  4 -
bridge/launchpad/launchpad.go      |  4 -
commands/bridge_token_add.go       | 15 ++---
doc/man/git-bug-bridge.1           |  2 
doc/md/git-bug_bridge.md           |  1 
misc/bash_completion/git-bug       | 84 ++++++++++++++++++++++++++++++++
misc/powershell_completion/git-bug | 28 ++++++++++
misc/zsh_completion/git-bug        | 46 +++++++++++++++++
11 files changed, 207 insertions(+), 68 deletions(-)

Detailed changes

bridge/bridges.go 🔗

@@ -3,13 +3,19 @@ package bridge
 
 import (
 	"github.com/MichaelMure/git-bug/bridge/core"
-	_ "github.com/MichaelMure/git-bug/bridge/github"
-	_ "github.com/MichaelMure/git-bug/bridge/gitlab"
-	_ "github.com/MichaelMure/git-bug/bridge/launchpad"
+	"github.com/MichaelMure/git-bug/bridge/github"
+	"github.com/MichaelMure/git-bug/bridge/gitlab"
+	"github.com/MichaelMure/git-bug/bridge/launchpad"
 	"github.com/MichaelMure/git-bug/cache"
 	"github.com/MichaelMure/git-bug/repository"
 )
 
+func init() {
+	core.Register(&github.Github{})
+	core.Register(&gitlab.Gitlab{})
+	core.Register(&launchpad.Launchpad{})
+}
+
 // Targets return all known bridge implementation target
 func Targets() []string {
 	return core.Targets()

bridge/core/token.go 🔗

@@ -14,7 +14,7 @@ const (
 	tokenKeyScopes       = "scopes"
 )
 
-// Token represent token related informations
+// Token holds an API access token data
 type Token struct {
 	Value  string
 	Target string
@@ -35,47 +35,46 @@ func NewToken(value, target string, global bool, scopes []string) *Token {
 // Validate ensure token important fields are valid
 func (t *Token) Validate() error {
 	if t.Value == "" {
-		return fmt.Errorf("missing token value")
+		return fmt.Errorf("missing value")
 	}
 	if t.Target == "" {
-		return fmt.Errorf("missing token target")
+		return fmt.Errorf("missing target")
+	}
+	if _, ok := bridgeImpl[t.Target]; !ok {
+		return fmt.Errorf("unknown target")
 	}
 	return nil
 }
 
 func loadToken(repo repository.RepoConfig, value string, global bool) (*Token, error) {
 	keyPrefix := fmt.Sprintf("git-bug.token.%s.", value)
-	var pairs map[string]string
-	var err error
 
-	// read token config pairs
+	readerFn := repo.ReadConfigs
 	if global {
-		pairs, err = repo.ReadGlobalConfigs(keyPrefix)
-		if err != nil {
-			return nil, err
-		}
-	} else {
-		pairs, err = repo.ReadConfigs(keyPrefix)
-		if err != nil {
-			return nil, err
-		}
+		readerFn = repo.ReadGlobalConfigs
+	}
+
+	// read token config pairs
+	configs, err := readerFn(keyPrefix)
+	if err != nil {
+		return nil, err
 	}
 
 	// trim key prefix
-	result := make(Configuration, len(pairs))
-	for key, value := range pairs {
-		key := strings.TrimPrefix(key, keyPrefix)
-		result[key] = value
+	for key, value := range configs {
+		newKey := strings.TrimPrefix(key, keyPrefix)
+		configs[newKey] = value
+		delete(configs, key)
 	}
 
 	var ok bool
 	token := &Token{Value: value, Global: global}
-	token.Target, ok = result[tokenKeyTarget]
+	token.Target, ok = configs[tokenKeyTarget]
 	if !ok {
 		return nil, fmt.Errorf("empty token key")
 	}
 
-	scopesString, ok := result[tokenKeyScopes]
+	scopesString, ok := configs[tokenKeyScopes]
 	if !ok {
 		return nil, fmt.Errorf("missing scopes config")
 	}
@@ -95,18 +94,14 @@ func GetGlobalToken(repo repository.RepoConfig, value string) (*Token, error) {
 }
 
 func listTokens(repo repository.RepoConfig, global bool) ([]string, error) {
-	var configs map[string]string
-	var err error
+	readerFn := repo.ReadConfigs
 	if global {
-		configs, err = repo.ReadGlobalConfigs(tokenConfigKeyPrefix + ".")
-		if err != nil {
-			return nil, err
-		}
-	} else {
-		configs, err = repo.ReadConfigs(tokenConfigKeyPrefix + ".")
-		if err != nil {
-			return nil, err
-		}
+		readerFn = repo.ReadGlobalConfigs
+	}
+
+	configs, err := readerFn(tokenConfigKeyPrefix + ".")
+	if err != nil {
+		return nil, err
 	}
 
 	re, err := regexp.Compile(tokenConfigKeyPrefix + `.([^.]+)`)
@@ -147,27 +142,19 @@ func ListGlobalTokens(repo repository.RepoConfig) ([]string, error) {
 }
 
 func storeToken(repo repository.RepoConfig, token *Token) error {
-	var store func(key, value string) error
+	storeFn := repo.StoreConfig
 	if token.Global {
-		store = repo.StoreGlobalConfig
-	} else {
-		store = repo.StoreConfig
+		storeFn = repo.StoreGlobalConfig
 	}
 
-	var err error
 	storeTargetKey := fmt.Sprintf("git-bug.token.%s.%s", token.Value, tokenKeyTarget)
-	err = store(storeTargetKey, token.Target)
+	err := storeFn(storeTargetKey, token.Target)
 	if err != nil {
 		return err
 	}
 
 	storeScopesKey := fmt.Sprintf("git-bug.token.%s.%s", token.Value, tokenKeyScopes)
-	err = store(storeScopesKey, strings.Join(token.Scopes, ","))
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return storeFn(storeScopesKey, strings.Join(token.Scopes, ","))
 }
 
 // StoreToken stores a token in the repo config

bridge/github/github.go 🔗

@@ -10,10 +10,6 @@ import (
 	"github.com/MichaelMure/git-bug/bridge/core"
 )
 
-func init() {
-	core.Register(&Github{})
-}
-
 type Github struct{}
 
 func (*Github) Target() string {

bridge/gitlab/gitlab.go 🔗

@@ -23,10 +23,6 @@ const (
 	defaultTimeout = 60 * time.Second
 )
 
-func init() {
-	core.Register(&Gitlab{})
-}
-
 type Gitlab struct{}
 
 func (*Gitlab) Target() string {

bridge/launchpad/launchpad.go 🔗

@@ -5,10 +5,6 @@ import (
 	"github.com/MichaelMure/git-bug/bridge/core"
 )
 
-func init() {
-	core.Register(&Launchpad{})
-}
-
 type Launchpad struct{}
 
 func (*Launchpad) Target() string {

commands/bridge_token_add.go 🔗

@@ -1,6 +1,7 @@
 package commands
 
 import (
+	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"github.com/MichaelMure/git-bug/bridge/core"
@@ -11,17 +12,15 @@ var (
 )
 
 func runBridgeTokenAdd(cmd *cobra.Command, args []string) error {
+	if err := bridgeToken.Validate(); err != nil {
+		return errors.Wrap(err, "invalid token")
+	}
+
 	if bridgeToken.Global {
-		return core.StoreToken(
-			repo,
-			&bridgeToken,
-		)
+		return core.StoreToken(repo, &bridgeToken)
 	}
 
-	return core.StoreGlobalToken(
-		repo,
-		&bridgeToken,
-	)
+	return core.StoreGlobalToken(repo, &bridgeToken)
 }
 
 var bridgeTokenAddCmd = &cobra.Command{

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\-bridge\-pull(1)\fP, \fBgit\-bug\-bridge\-push(1)\fP, \fBgit\-bug\-bridge\-rm(1)\fP
+\fBgit\-bug(1)\fP, \fBgit\-bug\-bridge\-configure(1)\fP, \fBgit\-bug\-bridge\-pull(1)\fP, \fBgit\-bug\-bridge\-push(1)\fP, \fBgit\-bug\-bridge\-rm(1)\fP, \fBgit\-bug\-bridge\-token(1)\fP

doc/md/git-bug_bridge.md 🔗

@@ -23,4 +23,5 @@ git-bug bridge [flags]
 * [git-bug bridge pull](git-bug_bridge_pull.md)	 - Pull updates.
 * [git-bug bridge push](git-bug_bridge_push.md)	 - Push updates.
 * [git-bug bridge rm](git-bug_bridge_rm.md)	 - Delete a configured bridge.
+* [git-bug bridge token](git-bug_bridge_token.md)	 - Configure and use bridge tokens.
 

misc/bash_completion/git-bug 🔗

@@ -400,6 +400,89 @@ _git-bug_bridge_rm()
     noun_aliases=()
 }
 
+_git-bug_bridge_token_add()
+{
+    last_command="git-bug_bridge_token_add"
+
+    command_aliases=()
+
+    commands=()
+
+    flags=()
+    two_word_flags=()
+    local_nonpersistent_flags=()
+    flags_with_completion=()
+    flags_completion=()
+
+    flags+=("--global")
+    flags+=("-g")
+    local_nonpersistent_flags+=("--global")
+    flags+=("--value=")
+    two_word_flags+=("--value")
+    two_word_flags+=("-v")
+    local_nonpersistent_flags+=("--value=")
+    flags+=("--target=")
+    two_word_flags+=("--target")
+    two_word_flags+=("-t")
+    local_nonpersistent_flags+=("--target=")
+    flags+=("--scopes=")
+    two_word_flags+=("--scopes")
+    two_word_flags+=("-s")
+    local_nonpersistent_flags+=("--scopes=")
+
+    must_have_one_flag=()
+    must_have_one_noun=()
+    noun_aliases=()
+}
+
+_git-bug_bridge_token_rm()
+{
+    last_command="git-bug_bridge_token_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_token()
+{
+    last_command="git-bug_bridge_token"
+
+    command_aliases=()
+
+    commands=()
+    commands+=("add")
+    commands+=("rm")
+
+    flags=()
+    two_word_flags=()
+    local_nonpersistent_flags=()
+    flags_with_completion=()
+    flags_completion=()
+
+    flags+=("--local")
+    flags+=("-l")
+    local_nonpersistent_flags+=("--local")
+    flags+=("--global")
+    flags+=("-g")
+    local_nonpersistent_flags+=("--global")
+
+    must_have_one_flag=()
+    must_have_one_noun=()
+    noun_aliases=()
+}
+
 _git-bug_bridge()
 {
     last_command="git-bug_bridge"
@@ -411,6 +494,7 @@ _git-bug_bridge()
     commands+=("pull")
     commands+=("push")
     commands+=("rm")
+    commands+=("token")
 
     flags=()
     two_word_flags=()

misc/powershell_completion/git-bug 🔗

@@ -22,6 +22,7 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock {
             [CompletionResult]::new('commands', 'commands', [CompletionResultType]::ParameterValue, 'Display available commands.')
             [CompletionResult]::new('comment', 'comment', [CompletionResultType]::ParameterValue, 'Display or add comments to a bug.')
             [CompletionResult]::new('deselect', 'deselect', [CompletionResultType]::ParameterValue, 'Clear the implicitly selected bug.')
+            [CompletionResult]::new('export', 'export', [CompletionResultType]::ParameterValue, '')
             [CompletionResult]::new('label', 'label', [CompletionResultType]::ParameterValue, 'Display, add or remove labels to/from a bug.')
             [CompletionResult]::new('ls', 'ls', [CompletionResultType]::ParameterValue, 'List bugs.')
             [CompletionResult]::new('ls-id', 'ls-id', [CompletionResultType]::ParameterValue, 'List bug identifiers.')
@@ -52,6 +53,7 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock {
             [CompletionResult]::new('pull', 'pull', [CompletionResultType]::ParameterValue, 'Pull updates.')
             [CompletionResult]::new('push', 'push', [CompletionResultType]::ParameterValue, 'Push updates.')
             [CompletionResult]::new('rm', 'rm', [CompletionResultType]::ParameterValue, 'Delete a configured bridge.')
+            [CompletionResult]::new('token', 'token', [CompletionResultType]::ParameterValue, 'Configure and use bridge tokens.')
             break
         }
         'git-bug;bridge;configure' {
@@ -83,6 +85,29 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock {
         'git-bug;bridge;rm' {
             break
         }
+        'git-bug;bridge;token' {
+            [CompletionResult]::new('-l', 'l', [CompletionResultType]::ParameterName, '')
+            [CompletionResult]::new('--local', 'local', [CompletionResultType]::ParameterName, '')
+            [CompletionResult]::new('-g', 'g', [CompletionResultType]::ParameterName, '')
+            [CompletionResult]::new('--global', 'global', [CompletionResultType]::ParameterName, '')
+            [CompletionResult]::new('add', 'add', [CompletionResultType]::ParameterValue, 'Configure and use bridge tokens.')
+            [CompletionResult]::new('rm', 'rm', [CompletionResultType]::ParameterValue, 'Configure and use bridge tokens.')
+            break
+        }
+        'git-bug;bridge;token;add' {
+            [CompletionResult]::new('-g', 'g', [CompletionResultType]::ParameterName, '')
+            [CompletionResult]::new('--global', 'global', [CompletionResultType]::ParameterName, '')
+            [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, '')
+            [CompletionResult]::new('--value', 'value', [CompletionResultType]::ParameterName, '')
+            [CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, '')
+            [CompletionResult]::new('--target', 'target', [CompletionResultType]::ParameterName, '')
+            [CompletionResult]::new('-s', 's', [CompletionResultType]::ParameterName, '')
+            [CompletionResult]::new('--scopes', 'scopes', [CompletionResultType]::ParameterName, '')
+            break
+        }
+        'git-bug;bridge;token;rm' {
+            break
+        }
         'git-bug;commands' {
             [CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Output the command description as well as Markdown compatible comment')
             [CompletionResult]::new('--pretty', 'pretty', [CompletionResultType]::ParameterName, 'Output the command description as well as Markdown compatible comment')
@@ -102,6 +127,9 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock {
         'git-bug;deselect' {
             break
         }
+        'git-bug;export' {
+            break
+        }
         'git-bug;label' {
             [CompletionResult]::new('add', 'add', [CompletionResultType]::ParameterValue, 'Add a label to a bug.')
             [CompletionResult]::new('rm', 'rm', [CompletionResultType]::ParameterValue, 'Remove a label from a bug.')

misc/zsh_completion/git-bug 🔗

@@ -118,6 +118,7 @@ function _git-bug_bridge {
       "pull:Pull updates."
       "push:Push updates."
       "rm:Delete a configured bridge."
+      "token:Configure and use bridge tokens."
     )
     _describe "command" commands
     ;;
@@ -136,6 +137,9 @@ function _git-bug_bridge {
   rm)
     _git-bug_bridge_rm
     ;;
+  token)
+    _git-bug_bridge_token
+    ;;
   esac
 }
 
@@ -164,6 +168,48 @@ function _git-bug_bridge_rm {
   _arguments
 }
 
+
+function _git-bug_bridge_token {
+  local -a commands
+
+  _arguments -C \
+    '(-l --local)'{-l,--local}'[]' \
+    '(-g --global)'{-g,--global}'[]' \
+    "1: :->cmnds" \
+    "*::arg:->args"
+
+  case $state in
+  cmnds)
+    commands=(
+      "add:Configure and use bridge tokens."
+      "rm:Configure and use bridge tokens."
+    )
+    _describe "command" commands
+    ;;
+  esac
+
+  case "$words[1]" in
+  add)
+    _git-bug_bridge_token_add
+    ;;
+  rm)
+    _git-bug_bridge_token_rm
+    ;;
+  esac
+}
+
+function _git-bug_bridge_token_add {
+  _arguments \
+    '(-g --global)'{-g,--global}'[]' \
+    '(-v --value)'{-v,--value}'[]:' \
+    '(-t --target)'{-t,--target}'[]:' \
+    '(*-s *--scopes)'{\*-s,\*--scopes}'[]:'
+}
+
+function _git-bug_bridge_token_rm {
+  _arguments
+}
+
 function _git-bug_commands {
   _arguments \
     '(-p --pretty)'{-p,--pretty}'[Output the command description as well as Markdown compatible comment]'