Merge pull request #200 from MichaelMure/bridge-token-stdin

bridge: support --token-stdin
This commit is contained in:
Michael Muré 2019-09-02 11:34:47 +02:00 committed by GitHub
commit 16af70894c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 99 additions and 21 deletions

View File

@ -31,10 +31,11 @@ var bridgeImpl map[string]reflect.Type
// BridgeParams holds parameters to simplify the bridge configuration without // BridgeParams holds parameters to simplify the bridge configuration without
// having to make terminal prompts. // having to make terminal prompts.
type BridgeParams struct { type BridgeParams struct {
Owner string Owner string
Project string Project string
URL string URL string
Token string Token string
TokenStdin bool
} }
// Bridge is a wrapper around a BridgeImpl that will bind low-level // Bridge is a wrapper around a BridgeImpl that will bind low-level

View File

@ -21,6 +21,7 @@ import (
"github.com/MichaelMure/git-bug/bridge/core" "github.com/MichaelMure/git-bug/bridge/core"
"github.com/MichaelMure/git-bug/repository" "github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util/interrupt"
) )
const ( const (
@ -37,13 +38,18 @@ var (
ErrBadProjectURL = errors.New("bad project url") ErrBadProjectURL = errors.New("bad project url")
) )
func (*Github) Configure(repo repository.RepoCommon, params core.BridgeParams) (core.Configuration, error) { func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams) (core.Configuration, error) {
conf := make(core.Configuration) conf := make(core.Configuration)
var err error var err error
var token string var token string
var owner string var owner string
var project string var project string
if (params.Token != "" || params.TokenStdin) &&
(params.URL == "" && (params.Project == "" || params.Owner == "")) {
return nil, fmt.Errorf("you must provide a project URL or Owner/Name to configure this bridge with a token")
}
// getting owner and project name // getting owner and project name
if params.Owner != "" && params.Project != "" { if params.Owner != "" && params.Project != "" {
// first try to use params if both or project and owner are provided // first try to use params if both or project and owner are provided
@ -85,6 +91,13 @@ func (*Github) Configure(repo repository.RepoCommon, params core.BridgeParams) (
if params.Token != "" { if params.Token != "" {
token = params.Token token = params.Token
} else if params.TokenStdin {
reader := bufio.NewReader(os.Stdin)
token, err = reader.ReadString('\n')
if err != nil {
return nil, fmt.Errorf("reading from stdin: %v", err)
}
token = strings.TrimSuffix(token, "\n")
} else { } else {
token, err = promptTokenOptions(owner, project) token, err = promptTokenOptions(owner, project)
if err != nil { if err != nil {
@ -106,6 +119,11 @@ func (*Github) Configure(repo repository.RepoCommon, params core.BridgeParams) (
conf[keyOwner] = owner conf[keyOwner] = owner
conf[keyProject] = project conf[keyProject] = project
err = g.ValidateConfig(conf)
if err != nil {
return nil, err
}
return conf, nil return conf, nil
} }
@ -505,6 +523,16 @@ func validateProject(owner, project, token string) (bool, error) {
} }
func promptPassword() (string, error) { func promptPassword() (string, error) {
termState, err := terminal.GetState(int(syscall.Stdin))
if err != nil {
return "", err
}
cancel := interrupt.RegisterCleaner(func() error {
return terminal.Restore(int(syscall.Stdin), termState)
})
defer cancel()
for { for {
fmt.Print("password: ") fmt.Print("password: ")
@ -526,6 +554,16 @@ func promptPassword() (string, error) {
} }
func prompt2FA() (string, error) { func prompt2FA() (string, error) {
termState, err := terminal.GetState(int(syscall.Stdin))
if err != nil {
return "", err
}
cancel := interrupt.RegisterCleaner(func() error {
return terminal.Restore(int(syscall.Stdin), termState)
})
defer cancel()
for { for {
fmt.Print("two-factor authentication code: ") fmt.Print("two-factor authentication code: ")

View File

@ -20,7 +20,7 @@ var (
ErrBadProjectURL = errors.New("bad project url") ErrBadProjectURL = errors.New("bad project url")
) )
func (*Gitlab) Configure(repo repository.RepoCommon, params core.BridgeParams) (core.Configuration, error) { func (g *Gitlab) Configure(repo repository.RepoCommon, params core.BridgeParams) (core.Configuration, error) {
if params.Project != "" { if params.Project != "" {
fmt.Println("warning: --project is ineffective for a gitlab bridge") fmt.Println("warning: --project is ineffective for a gitlab bridge")
} }
@ -33,6 +33,10 @@ func (*Gitlab) Configure(repo repository.RepoCommon, params core.BridgeParams) (
var url string var url string
var token string var token string
if (params.Token != "" || params.TokenStdin) && params.URL == "" {
return nil, fmt.Errorf("you must provide a project URL to configure this bridge with a token")
}
// get project url // get project url
if params.URL != "" { if params.URL != "" {
url = params.URL url = params.URL
@ -54,6 +58,13 @@ func (*Gitlab) Configure(repo repository.RepoCommon, params core.BridgeParams) (
// get user token // get user token
if params.Token != "" { if params.Token != "" {
token = params.Token token = params.Token
} else if params.TokenStdin {
reader := bufio.NewReader(os.Stdin)
token, err = reader.ReadString('\n')
if err != nil {
return nil, fmt.Errorf("reading from stdin: %v", err)
}
token = strings.TrimSuffix(token, "\n")
} else { } else {
token, err = promptToken() token, err = promptToken()
if err != nil { if err != nil {
@ -71,10 +82,15 @@ func (*Gitlab) Configure(repo repository.RepoCommon, params core.BridgeParams) (
conf[keyToken] = token conf[keyToken] = token
conf[core.KeyTarget] = target conf[core.KeyTarget] = target
err = g.ValidateConfig(conf)
if err != nil {
return nil, err
}
return conf, nil return conf, nil
} }
func (*Gitlab) ValidateConfig(conf core.Configuration) error { func (g *Gitlab) ValidateConfig(conf core.Configuration) error {
if v, ok := conf[core.KeyTarget]; !ok { if v, ok := conf[core.KeyTarget]; !ok {
return fmt.Errorf("missing %s key", core.KeyTarget) return fmt.Errorf("missing %s key", core.KeyTarget)
} else if v != target { } else if v != target {

View File

@ -22,7 +22,7 @@ const (
defaultTimeout = 60 * time.Second defaultTimeout = 60 * time.Second
) )
func (*Launchpad) Configure(repo repository.RepoCommon, params core.BridgeParams) (core.Configuration, error) { func (l *Launchpad) Configure(repo repository.RepoCommon, params core.BridgeParams) (core.Configuration, error) {
if params.Token != "" { if params.Token != "" {
fmt.Println("warning: --token is ineffective for a Launchpad bridge") fmt.Println("warning: --token is ineffective for a Launchpad bridge")
} }
@ -63,6 +63,12 @@ func (*Launchpad) Configure(repo repository.RepoCommon, params core.BridgeParams
conf[keyProject] = project conf[keyProject] = project
conf[core.KeyTarget] = target conf[core.KeyTarget] = target
err = l.ValidateConfig(conf)
if err != nil {
return nil, err
}
return conf, nil return conf, nil
} }

View File

@ -6,10 +6,8 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
"syscall"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"golang.org/x/crypto/ssh/terminal"
"github.com/MichaelMure/git-bug/bridge" "github.com/MichaelMure/git-bug/bridge"
"github.com/MichaelMure/git-bug/bridge/core" "github.com/MichaelMure/git-bug/bridge/core"
@ -36,15 +34,10 @@ func runBridgeConfigure(cmd *cobra.Command, args []string) error {
defer backend.Close() defer backend.Close()
interrupt.RegisterCleaner(backend.Close) interrupt.RegisterCleaner(backend.Close)
termState, err := terminal.GetState(int(syscall.Stdin)) if (bridgeParams.TokenStdin || bridgeParams.Token != "") && (bridgeConfigureName == "" || bridgeConfigureTarget == "") {
if err != nil { return fmt.Errorf("you must provide a bridge name and target to configure a bridge with a token")
return err
} }
interrupt.RegisterCleaner(func() error {
return terminal.Restore(int(syscall.Stdin), termState)
})
if bridgeConfigureTarget == "" { if bridgeConfigureTarget == "" {
bridgeConfigureTarget, err = promptTarget() bridgeConfigureTarget, err = promptTarget()
if err != nil { if err != nil {
@ -192,6 +185,7 @@ func init() {
bridgeConfigureCmd.Flags().StringVarP(&bridgeParams.URL, "url", "u", "", "The URL of the target repository") bridgeConfigureCmd.Flags().StringVarP(&bridgeParams.URL, "url", "u", "", "The URL of the target repository")
bridgeConfigureCmd.Flags().StringVarP(&bridgeParams.Owner, "owner", "o", "", "The owner of the target repository") bridgeConfigureCmd.Flags().StringVarP(&bridgeParams.Owner, "owner", "o", "", "The owner of the target repository")
bridgeConfigureCmd.Flags().StringVarP(&bridgeParams.Token, "token", "T", "", "The authentication token for the API") bridgeConfigureCmd.Flags().StringVarP(&bridgeParams.Token, "token", "T", "", "The authentication token for the API")
bridgeConfigureCmd.Flags().BoolVar(&bridgeParams.TokenStdin, "token-stdin", false, "Will read the token from stdin and ignore --token")
bridgeConfigureCmd.Flags().StringVarP(&bridgeParams.Project, "project", "p", "", "The name of the target repository") bridgeConfigureCmd.Flags().StringVarP(&bridgeParams.Project, "project", "p", "", "The name of the target repository")
bridgeConfigureCmd.Flags().SortFlags = false bridgeConfigureCmd.Flags().SortFlags = false
} }

View File

@ -47,6 +47,10 @@ Token configuration can be directly passed with the \-\-token flag or in the ter
\fB\-T\fP, \fB\-\-token\fP="" \fB\-T\fP, \fB\-\-token\fP=""
The authentication token for the API The authentication token for the API
.PP
\fB\-\-token\-stdin\fP[=false]
Will read the token from stdin and ignore \-\-token
.PP .PP
\fB\-p\fP, \fB\-\-project\fP="" \fB\-p\fP, \fB\-\-project\fP=""
The name of the target repository The name of the target repository
@ -91,7 +95,7 @@ Private:
Enter token: 87cf5c03b64029f18ea5f9ca5679daa08ccbd700 Enter token: 87cf5c03b64029f18ea5f9ca5679daa08ccbd700
Successfully configured bridge: default Successfully configured bridge: default
# For Github # For GitHub
git bug bridge configure \\ git bug bridge configure \\
\-\-name=default \\ \-\-name=default \\
\-\-target=github \\ \-\-target=github \\
@ -103,7 +107,14 @@ git bug bridge configure \\
git bug bridge configure \\ git bug bridge configure \\
\-\-name=default \\ \-\-name=default \\
\-\-target=launchpad\-preview \\ \-\-target=launchpad\-preview \\
\-\-url=https://bugs.launchpad.net/ubuntu/ \-\-url=https://bugs.launchpad.net/ubuntu/
# For Gitlab
git bug bridge configure \\
\-\-name=default \\
\-\-target=github \\
\-\-url=https://github.com/michaelmure/git\-bug \\
\-\-token=$(TOKEN)
.fi .fi
.RE .RE

View File

@ -45,7 +45,7 @@ Private:
Enter token: 87cf5c03b64029f18ea5f9ca5679daa08ccbd700 Enter token: 87cf5c03b64029f18ea5f9ca5679daa08ccbd700
Successfully configured bridge: default Successfully configured bridge: default
# For Github # For GitHub
git bug bridge configure \ git bug bridge configure \
--name=default \ --name=default \
--target=github \ --target=github \
@ -57,7 +57,14 @@ git bug bridge configure \
git bug bridge configure \ git bug bridge configure \
--name=default \ --name=default \
--target=launchpad-preview \ --target=launchpad-preview \
--url=https://bugs.launchpad.net/ubuntu/ --url=https://bugs.launchpad.net/ubuntu/
# For Gitlab
git bug bridge configure \
--name=default \
--target=github \
--url=https://github.com/michaelmure/git-bug \
--token=$(TOKEN)
``` ```
### Options ### Options
@ -68,6 +75,7 @@ git bug bridge configure \
-u, --url string The URL of the target repository -u, --url string The URL of the target repository
-o, --owner string The owner of the target repository -o, --owner string The owner of the target repository
-T, --token string The authentication token for the API -T, --token string The authentication token for the API
--token-stdin Will read the token from stdin and ignore --token
-p, --project string The name of the target repository -p, --project string The name of the target repository
-h, --help help for configure -h, --help help for configure
``` ```

View File

@ -321,6 +321,8 @@ _git-bug_bridge_configure()
two_word_flags+=("--token") two_word_flags+=("--token")
two_word_flags+=("-T") two_word_flags+=("-T")
local_nonpersistent_flags+=("--token=") local_nonpersistent_flags+=("--token=")
flags+=("--token-stdin")
local_nonpersistent_flags+=("--token-stdin")
flags+=("--project=") flags+=("--project=")
two_word_flags+=("--project") two_word_flags+=("--project")
two_word_flags+=("-p") two_word_flags+=("-p")

View File

@ -65,6 +65,7 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock {
[CompletionResult]::new('--owner', 'owner', [CompletionResultType]::ParameterName, 'The owner of the target repository') [CompletionResult]::new('--owner', 'owner', [CompletionResultType]::ParameterName, 'The owner of the target repository')
[CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'The authentication token for the API') [CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'The authentication token for the API')
[CompletionResult]::new('--token', 'token', [CompletionResultType]::ParameterName, 'The authentication token for the API') [CompletionResult]::new('--token', 'token', [CompletionResultType]::ParameterName, 'The authentication token for the API')
[CompletionResult]::new('--token-stdin', 'token-stdin', [CompletionResultType]::ParameterName, 'Will read the token from stdin and ignore --token')
[CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'The name of the target repository') [CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'The name of the target repository')
[CompletionResult]::new('--project', 'project', [CompletionResultType]::ParameterName, 'The name of the target repository') [CompletionResult]::new('--project', 'project', [CompletionResultType]::ParameterName, 'The name of the target repository')
break break

View File

@ -146,6 +146,7 @@ function _git-bug_bridge_configure {
'(-u --url)'{-u,--url}'[The URL of the target repository]:' \ '(-u --url)'{-u,--url}'[The URL of the target repository]:' \
'(-o --owner)'{-o,--owner}'[The owner of the target repository]:' \ '(-o --owner)'{-o,--owner}'[The owner of the target repository]:' \
'(-T --token)'{-T,--token}'[The authentication token for the API]:' \ '(-T --token)'{-T,--token}'[The authentication token for the API]:' \
'--token-stdin[Will read the token from stdin and ignore --token]' \
'(-p --project)'{-p,--project}'[The name of the target repository]:' '(-p --project)'{-p,--project}'[The name of the target repository]:'
} }