X Tutup
Skip to content

Commit 344906b

Browse files
committed
Test SSH config parser
1 parent 79e8766 commit 344906b

File tree

4 files changed

+75
-43
lines changed

4 files changed

+75
-43
lines changed

command/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"os"
66

77
"github.com/github/gh-cli/context"
8+
"github.com/github/gh-cli/git"
89
"github.com/spf13/cobra"
910
)
1011

@@ -26,6 +27,7 @@ func initContext() {
2627
repo = os.Getenv("GH_REPO")
2728
}
2829
ctx.SetBaseRepo(repo)
30+
git.InitSSHAliasMap(nil)
2931
}
3032

3133
// RootCmd is the entry point of command-line execution

git/ssh_config.go

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package git
22

33
import (
44
"bufio"
5+
"io"
56
"os"
67
"path/filepath"
78
"regexp"
@@ -10,13 +11,19 @@ import (
1011
"github.com/mitchellh/go-homedir"
1112
)
1213

13-
const (
14-
hostReStr = "(?i)^[ \t]*(host|hostname)[ \t]+(.+)$"
14+
var (
15+
sshHostRE,
16+
sshTokenRE *regexp.Regexp
1517
)
1618

17-
type SSHConfig map[string]string
19+
func init() {
20+
sshHostRE = regexp.MustCompile("(?i)^[ \t]*(host|hostname)[ \t]+(.+)$")
21+
sshTokenRE = regexp.MustCompile(`%[%h]`)
22+
}
23+
24+
type sshAliasMap map[string]string
1825

19-
func newSSHConfigReader() *SSHConfigReader {
26+
func sshParseFiles() sshAliasMap {
2027
configFiles := []string{
2128
"/etc/ssh_config",
2229
"/etc/ssh/ssh_config",
@@ -25,38 +32,33 @@ func newSSHConfigReader() *SSHConfigReader {
2532
userConfig := filepath.Join(homedir, ".ssh", "config")
2633
configFiles = append([]string{userConfig}, configFiles...)
2734
}
28-
return &SSHConfigReader{
29-
Files: configFiles,
30-
}
31-
}
3235

33-
type SSHConfigReader struct {
34-
Files []string
36+
openFiles := []io.Reader{}
37+
for _, file := range configFiles {
38+
f, err := os.Open(file)
39+
if err != nil {
40+
continue
41+
}
42+
defer f.Close()
43+
openFiles = append(openFiles, f)
44+
}
45+
return sshParse(openFiles...)
3546
}
3647

37-
func (r *SSHConfigReader) Read() SSHConfig {
38-
config := make(SSHConfig)
39-
hostRe := regexp.MustCompile(hostReStr)
40-
41-
for _, filename := range r.Files {
42-
r.readFile(config, hostRe, filename)
48+
func sshParse(r ...io.Reader) sshAliasMap {
49+
config := sshAliasMap{}
50+
for _, file := range r {
51+
sshParseConfig(config, file)
4352
}
44-
4553
return config
4654
}
4755

48-
func (r *SSHConfigReader) readFile(c SSHConfig, re *regexp.Regexp, f string) error {
49-
file, err := os.Open(f)
50-
if err != nil {
51-
return err
52-
}
53-
defer file.Close()
54-
56+
func sshParseConfig(c sshAliasMap, file io.Reader) error {
5557
hosts := []string{"*"}
5658
scanner := bufio.NewScanner(file)
5759
for scanner.Scan() {
5860
line := scanner.Text()
59-
match := re.FindStringSubmatch(line)
61+
match := sshHostRE.FindStringSubmatch(line)
6062
if match == nil {
6163
continue
6264
}
@@ -67,7 +69,7 @@ func (r *SSHConfigReader) readFile(c SSHConfig, re *regexp.Regexp, f string) err
6769
} else {
6870
for _, host := range hosts {
6971
for _, name := range names {
70-
c[host] = expandTokens(name, host)
72+
c[host] = sshExpandTokens(name, host)
7173
}
7274
}
7375
}
@@ -76,9 +78,8 @@ func (r *SSHConfigReader) readFile(c SSHConfig, re *regexp.Regexp, f string) err
7678
return scanner.Err()
7779
}
7880

79-
func expandTokens(text, host string) string {
80-
re := regexp.MustCompile(`%[%h]`)
81-
return re.ReplaceAllStringFunc(text, func(match string) string {
81+
func sshExpandTokens(text, host string) string {
82+
return sshTokenRE.ReplaceAllStringFunc(text, func(match string) string {
8283
switch match {
8384
case "%h":
8485
return host

git/ssh_config_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package git
2+
3+
import (
4+
"reflect"
5+
"strings"
6+
"testing"
7+
)
8+
9+
// TODO: extract assertion helpers into a shared package
10+
func eq(t *testing.T, got interface{}, expected interface{}) {
11+
if !reflect.DeepEqual(got, expected) {
12+
t.Errorf("expected: %v, got: %v", expected, got)
13+
}
14+
}
15+
16+
func Test_sshParse(t *testing.T) {
17+
m := sshParse(strings.NewReader(`
18+
Host foo bar
19+
HostName example.com
20+
`), strings.NewReader(`
21+
Host bar baz
22+
hostname %%%h.net%%
23+
`))
24+
eq(t, m["foo"], "example.com")
25+
eq(t, m["bar"], "%bar.net%")
26+
eq(t, m["nonexist"], "")
27+
}

git/url.go

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,12 @@ import (
77
)
88

99
var (
10-
cachedSSHConfig SSHConfig
10+
cachedSSHConfig sshAliasMap
1111
protocolRe = regexp.MustCompile("^[a-zA-Z_+-]+://")
1212
)
1313

14-
type URLParser struct {
15-
SSHConfig SSHConfig
16-
}
17-
18-
func (p *URLParser) Parse(rawURL string) (u *url.URL, err error) {
14+
// ParseURL normalizes git remote urls
15+
func ParseURL(rawURL string) (u *url.URL, err error) {
1916
if !protocolRe.MatchString(rawURL) &&
2017
strings.Contains(rawURL, ":") &&
2118
// not a Windows path
@@ -44,7 +41,10 @@ func (p *URLParser) Parse(rawURL string) (u *url.URL, err error) {
4441
u.Host = u.Host[0:idx]
4542
}
4643

47-
sshHost := p.SSHConfig[u.Host]
44+
if cachedSSHConfig == nil {
45+
return
46+
}
47+
sshHost := cachedSSHConfig[u.Host]
4848
// ignore replacing host that fixes for limited network
4949
// https://help.github.com/articles/using-ssh-over-the-https-port
5050
ignoredHost := u.Host == "github.com" && sshHost == "ssh.github.com"
@@ -55,12 +55,14 @@ func (p *URLParser) Parse(rawURL string) (u *url.URL, err error) {
5555
return
5656
}
5757

58-
func ParseURL(rawURL string) (u *url.URL, err error) {
59-
if cachedSSHConfig == nil {
60-
cachedSSHConfig = newSSHConfigReader().Read()
58+
// InitSSHAliasMap prepares globally cached SSH hostname alias mappings
59+
func InitSSHAliasMap(m map[string]string) {
60+
if m == nil {
61+
cachedSSHConfig = sshParseFiles()
62+
return
63+
}
64+
cachedSSHConfig = sshAliasMap{}
65+
for k, v := range m {
66+
cachedSSHConfig[k] = v
6167
}
62-
63-
p := &URLParser{cachedSSHConfig}
64-
65-
return p.Parse(rawURL)
6668
}

0 commit comments

Comments
 (0)
X Tutup