зеркало из https://github.com/mislav/hub.git
Merge pull request #620 from github/gh_yaml
Refactor to `configService` to read & write `Config`
This commit is contained in:
Коммит
254bf7c7e7
|
@ -59,8 +59,8 @@ func transformCloneArgs(args *Args) {
|
|||
name, owner := parseCloneNameAndOwner(a)
|
||||
var host *github.Host
|
||||
if owner == "" {
|
||||
configs := github.CurrentConfigs()
|
||||
h, err := configs.DefaultHost()
|
||||
config := github.CurrentConfig()
|
||||
h, err := config.DefaultHost()
|
||||
if err != nil {
|
||||
utils.Check(github.FormatError("cloning repository", err))
|
||||
}
|
||||
|
|
|
@ -73,8 +73,8 @@ func create(command *Command, args *Args) {
|
|||
newRepoName = args.FirstParam()
|
||||
}
|
||||
|
||||
configs := github.CurrentConfigs()
|
||||
host, err := configs.DefaultHost()
|
||||
config := github.CurrentConfig()
|
||||
host, err := config.DefaultHost()
|
||||
if err != nil {
|
||||
utils.Check(github.FormatError("creating repository", err))
|
||||
}
|
||||
|
|
|
@ -2,9 +2,10 @@ package commands
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/github/hub/github"
|
||||
"github.com/github/hub/utils"
|
||||
"os"
|
||||
)
|
||||
|
||||
var cmdFork = &Command{
|
||||
|
@ -41,8 +42,8 @@ func fork(cmd *Command, args *Args) {
|
|||
utils.Check(fmt.Errorf("Error: repository under 'origin' remote is not a GitHub project"))
|
||||
}
|
||||
|
||||
configs := github.CurrentConfigs()
|
||||
host, err := configs.PromptForHost(project.Host)
|
||||
config := github.CurrentConfig()
|
||||
host, err := config.PromptForHost(project.Host)
|
||||
if err != nil {
|
||||
utils.Check(github.FormatError("forking repository", err))
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ func pullRequest(cmd *Command, args *Args) {
|
|||
baseProject, err := localRepo.MainProject()
|
||||
utils.Check(err)
|
||||
|
||||
host, err := github.CurrentConfigs().PromptForHost(baseProject.Host)
|
||||
host, err := github.CurrentConfig().PromptForHost(baseProject.Host)
|
||||
if err != nil {
|
||||
utils.Check(github.FormatError("creating pull request", err))
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ func transformRemoteArgs(args *Args) {
|
|||
isPriavte := parseRemotePrivateFlag(args)
|
||||
if len(words) == 2 && words[1] == "origin" {
|
||||
// Origin special case triggers default user/repo
|
||||
host, err := github.CurrentConfigs().DefaultHost()
|
||||
host, err := github.CurrentConfig().DefaultHost()
|
||||
if err != nil {
|
||||
utils.Check(github.FormatError("adding remote", err))
|
||||
}
|
||||
|
|
|
@ -443,7 +443,7 @@ func (client *Client) FindOrCreateToken(user, password, twoFactorCode string) (t
|
|||
|
||||
func (client *Client) api() (c *octokit.Client, err error) {
|
||||
if client.Host.AccessToken == "" {
|
||||
host, e := CurrentConfigs().PromptForHost(client.Host.Host)
|
||||
host, e := CurrentConfig().PromptForHost(client.Host.Host)
|
||||
if e != nil {
|
||||
err = e
|
||||
return
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/github/hub/utils"
|
||||
"github.com/howeyc/gopass"
|
||||
)
|
||||
|
@ -24,11 +23,11 @@ type Host struct {
|
|||
Protocol string `toml:"protocol"`
|
||||
}
|
||||
|
||||
type Configs struct {
|
||||
type Config struct {
|
||||
Hosts []Host `toml:"hosts"`
|
||||
}
|
||||
|
||||
func (c *Configs) PromptForHost(host string) (h *Host, err error) {
|
||||
func (c *Config) PromptForHost(host string) (h *Host, err error) {
|
||||
h = c.Find(host)
|
||||
if h != nil {
|
||||
return
|
||||
|
@ -65,12 +64,12 @@ func (c *Configs) PromptForHost(host string) (h *Host, err error) {
|
|||
Protocol: "https",
|
||||
}
|
||||
c.Hosts = append(c.Hosts, *h)
|
||||
err = saveTo(configsFile(), c)
|
||||
err = newConfigService().Save(configsFile(), c)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Configs) PromptForUser() (user string) {
|
||||
func (c *Config) PromptForUser() (user string) {
|
||||
user = os.Getenv("GITHUB_USER")
|
||||
if user != "" {
|
||||
return
|
||||
|
@ -82,7 +81,7 @@ func (c *Configs) PromptForUser() (user string) {
|
|||
return
|
||||
}
|
||||
|
||||
func (c *Configs) PromptForPassword(host, user string) (pass string) {
|
||||
func (c *Config) PromptForPassword(host, user string) (pass string) {
|
||||
pass = os.Getenv("GITHUB_PASSWORD")
|
||||
if pass != "" {
|
||||
return
|
||||
|
@ -98,12 +97,12 @@ func (c *Configs) PromptForPassword(host, user string) (pass string) {
|
|||
return
|
||||
}
|
||||
|
||||
func (c *Configs) PromptForOTP() string {
|
||||
func (c *Config) PromptForOTP() string {
|
||||
fmt.Print("two-factor authentication code: ")
|
||||
return c.scanLine()
|
||||
}
|
||||
|
||||
func (c *Configs) scanLine() string {
|
||||
func (c *Config) scanLine() string {
|
||||
var line string
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
if scanner.Scan() {
|
||||
|
@ -114,7 +113,7 @@ func (c *Configs) scanLine() string {
|
|||
return line
|
||||
}
|
||||
|
||||
func (c *Configs) Find(host string) *Host {
|
||||
func (c *Config) Find(host string) *Host {
|
||||
for _, h := range c.Hosts {
|
||||
if h.Host == host {
|
||||
return &h
|
||||
|
@ -124,61 +123,7 @@ func (c *Configs) Find(host string) *Host {
|
|||
return nil
|
||||
}
|
||||
|
||||
func saveTo(filename string, v interface{}) error {
|
||||
err := os.MkdirAll(filepath.Dir(filename), 0771)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
enc := toml.NewEncoder(f)
|
||||
return enc.Encode(v)
|
||||
}
|
||||
|
||||
func loadFrom(filename string, c *Configs) (err error) {
|
||||
_, err = toml.DecodeFile(filename, c)
|
||||
return
|
||||
}
|
||||
|
||||
func configsFile() string {
|
||||
configsFile := os.Getenv("GH_CONFIG")
|
||||
if configsFile == "" {
|
||||
configsFile = defaultConfigsFile
|
||||
}
|
||||
|
||||
return configsFile
|
||||
}
|
||||
|
||||
func CurrentConfigs() *Configs {
|
||||
c := &Configs{}
|
||||
|
||||
configFile := configsFile()
|
||||
err := loadFrom(configFile, c)
|
||||
if err != nil {
|
||||
// load from YAML
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Configs) DefaultHost() (host *Host, err error) {
|
||||
if GitHubHostEnv != "" {
|
||||
host, err = c.PromptForHost(GitHubHostEnv)
|
||||
} else if len(c.Hosts) > 0 {
|
||||
host = c.selectHost()
|
||||
} else {
|
||||
host, err = c.PromptForHost(DefaultGitHubHost())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Configs) selectHost() *Host {
|
||||
func (c *Config) selectHost() *Host {
|
||||
options := len(c.Hosts)
|
||||
|
||||
if options == 1 {
|
||||
|
@ -201,12 +146,40 @@ func (c *Configs) selectHost() *Host {
|
|||
return &c.Hosts[i-1]
|
||||
}
|
||||
|
||||
func (c *Configs) Save() error {
|
||||
return saveTo(configsFile(), c)
|
||||
func configsFile() string {
|
||||
configsFile := os.Getenv("GH_CONFIG")
|
||||
if configsFile == "" {
|
||||
configsFile = defaultConfigsFile
|
||||
}
|
||||
|
||||
return configsFile
|
||||
}
|
||||
|
||||
func CurrentConfig() *Config {
|
||||
c := &Config{}
|
||||
|
||||
err := newConfigService().Load(configsFile(), c)
|
||||
if err != nil {
|
||||
// load from YAML
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Config) DefaultHost() (host *Host, err error) {
|
||||
if GitHubHostEnv != "" {
|
||||
host, err = c.PromptForHost(GitHubHostEnv)
|
||||
} else if len(c.Hosts) > 0 {
|
||||
host = c.selectHost()
|
||||
} else {
|
||||
host, err = c.PromptForHost(DefaultGitHubHost())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Public for testing purpose
|
||||
func CreateTestConfigs(user, token string) *Configs {
|
||||
func CreateTestConfigs(user, token string) *Config {
|
||||
f, _ := ioutil.TempFile("", "test-config")
|
||||
defaultConfigsFile = f.Name()
|
||||
|
||||
|
@ -216,8 +189,11 @@ func CreateTestConfigs(user, token string) *Configs {
|
|||
Host: GitHubHost,
|
||||
}
|
||||
|
||||
c := &Configs{Hosts: []Host{host}}
|
||||
saveTo(f.Name(), c)
|
||||
c := &Config{Hosts: []Host{host}}
|
||||
err := newConfigService().Save(f.Name(), c)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package github
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
type configDecoder interface {
|
||||
Decode(r io.Reader, v interface{}) error
|
||||
}
|
||||
|
||||
type tomlConfigDecoder struct {
|
||||
}
|
||||
|
||||
func (t *tomlConfigDecoder) Decode(r io.Reader, v interface{}) error {
|
||||
_, err := toml.DecodeReader(r, v)
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package github
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
type configEncoder interface {
|
||||
Encode(w io.Writer, v interface{}) error
|
||||
}
|
||||
|
||||
type tomlConfigEncoder struct {
|
||||
}
|
||||
|
||||
func (t *tomlConfigEncoder) Encode(w io.Writer, v interface{}) error {
|
||||
enc := toml.NewEncoder(w)
|
||||
return enc.Encode(v)
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package github
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func newConfigService() *configService {
|
||||
return &configService{
|
||||
Encoder: &tomlConfigEncoder{},
|
||||
Decoder: &tomlConfigDecoder{},
|
||||
}
|
||||
}
|
||||
|
||||
type configService struct {
|
||||
Encoder configEncoder
|
||||
Decoder configDecoder
|
||||
}
|
||||
|
||||
func (s *configService) Save(filename string, c *Config) error {
|
||||
err := os.MkdirAll(filepath.Dir(filename), 0771)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
return s.Encoder.Encode(w, c)
|
||||
}
|
||||
|
||||
func (s *configService) Load(filename string, c *Config) error {
|
||||
r, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
return s.Decoder.Decode(r, c)
|
||||
}
|
|
@ -10,12 +10,12 @@ import (
|
|||
"github.com/github/hub/fixtures"
|
||||
)
|
||||
|
||||
func TestConfigs_loadFrom(t *testing.T) {
|
||||
testConfigs := fixtures.SetupTestConfigs()
|
||||
defer testConfigs.TearDown()
|
||||
func TestConfigService_Load(t *testing.T) {
|
||||
testConfig := fixtures.SetupTestConfigs()
|
||||
defer testConfig.TearDown()
|
||||
|
||||
cc := &Configs{}
|
||||
err := loadFrom(testConfigs.Path, cc)
|
||||
cc := &Config{}
|
||||
err := newConfigService().Load(testConfig.Path, cc)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
assert.Equal(t, 1, len(cc.Hosts))
|
||||
|
@ -26,7 +26,7 @@ func TestConfigs_loadFrom(t *testing.T) {
|
|||
assert.Equal(t, "http", host.Protocol)
|
||||
}
|
||||
|
||||
func TestConfigs_saveTo(t *testing.T) {
|
||||
func TestConfigService_Save(t *testing.T) {
|
||||
file, _ := ioutil.TempFile("", "test-gh-config-")
|
||||
defer os.RemoveAll(file.Name())
|
||||
|
||||
|
@ -36,9 +36,9 @@ func TestConfigs_saveTo(t *testing.T) {
|
|||
AccessToken: "123",
|
||||
Protocol: "https",
|
||||
}
|
||||
c := Configs{Hosts: []Host{host}}
|
||||
c := Config{Hosts: []Host{host}}
|
||||
|
||||
err := saveTo(file.Name(), &c)
|
||||
err := newConfigService().Save(file.Name(), &c)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
b, _ := ioutil.ReadFile(file.Name())
|
|
@ -151,7 +151,7 @@ func newProject(owner, name, host, protocol string) *Project {
|
|||
protocol = ""
|
||||
}
|
||||
if protocol == "" {
|
||||
h := CurrentConfigs().Find(host)
|
||||
h := CurrentConfig().Find(host)
|
||||
if h != nil {
|
||||
protocol = h.Protocol
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ func newProject(owner, name, host, protocol string) *Project {
|
|||
}
|
||||
|
||||
if owner == "" {
|
||||
h := CurrentConfigs().Find(host)
|
||||
h := CurrentConfig().Find(host)
|
||||
if h != nil {
|
||||
owner = h.User
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче