Skip to content

Commit 831c604

Browse files
committed
Add additional Provider support
By default, only 3 providers are available. This PR adds the ability for a user to set additional Git providers with the global Git config. The Git config structure includes a base URL as an argument, and commit prefix and path prefix keys and values. [open "https://git.mydomain.dev"] commitprefix = commit pathprefix = tree It can be set by editing the above into a .gitconfig or with the Git CLI. git config --global open.https://git.mydomain.dev.commitprefix commit git config --global open.https://git.mydomain.dev.pathprefix tree The resulting additional providers will be merged with the default providers and work with `git open` calls.
1 parent 59d953a commit 831c604

File tree

9 files changed

+183
-13
lines changed

9 files changed

+183
-13
lines changed

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,35 @@ Open a different repository than `cwd`.
2828
$ git -C ~/src/my-repo open
2929
```
3030

31+
### Providers
32+
33+
By default, three providers [github.com](https://github.com), [gitlab.com](https://gitlab.com) and [bitbucket.org](https://bitbucket.org) are supported.
34+
35+
To add custom Git providers and their URLs, set their values within the global `git config`.
36+
37+
```ini
38+
[open "https://git.mydomain.dev"]
39+
commitprefix = commit
40+
pathprefix = tree
41+
```
42+
43+
This can also be set using the `git` CLI.
44+
45+
```console
46+
$ git config --global open.https://git.mydomain.dev.commitprefix commit
47+
$ git config --global open.https://git.mydomain.dev.pathprefix tree
48+
```
49+
50+
`commitprefix` and `pathprefix` are used to template the URI for your provider.
51+
52+
```go
53+
fmt.Println(host + "/" + repository + "/" + commitprefix )
54+
// https://git.mydomain.dev/<repository>/commit
55+
56+
fmt.Println(host + "/" + repository + "/" + pathprefix )
57+
// https://git.mydomain.dev/<repository>/tree
58+
```
59+
3160
## Installation
3261

3362
Install with `brew`.

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ go 1.22
44

55
require (
66
github.com/arbourd/git-get v0.6.1
7+
github.com/google/go-cmp v0.6.0
78
github.com/ldez/go-git-cmd-wrapper/v2 v2.6.0
89
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
github.com/arbourd/git-get v0.6.1 h1:IYMN6gbtJcADrUpwflVR/5ERwovQ1F4nENSh7CaJnQ4=
22
github.com/arbourd/git-get v0.6.1/go.mod h1:haRy2V/Odc+rDhrJhy098yNLZt0qNYu8Q752SGCHz9M=
3+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
4+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
35
github.com/ldez/go-git-cmd-wrapper/v2 v2.6.0 h1:o5QIusOiH9phm1gY2UGO6JQjYSPFYbgFCcntOigBvMg=
46
github.com/ldez/go-git-cmd-wrapper/v2 v2.6.0/go.mod h1:whnaSah+AmezZS8vwp8FyFzEBHZCLKywWILUj5D8Jq0=

main.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,20 @@ import (
1010
func main() {
1111
arg, err := processArgs(os.Args)
1212
if err != nil {
13-
fmt.Printf("Error: \"%s\"\n", err)
13+
fmt.Printf("error: \"%s\"\n", err)
1414
os.Exit(1)
1515
}
1616

1717
url, err := open.GetURL(arg)
1818
if err != nil {
19-
fmt.Printf("Error: \"%s\"\n", err)
19+
fmt.Printf("error: \"%s\"\n", err)
2020
os.Exit(1)
2121
}
2222

2323
fmt.Printf("Opening %s in your browser.\n", url)
2424
err = open.InBrowser(url)
2525
if err != nil {
26-
fmt.Printf("Error: unable to open in browser: \"%s\"\n", err)
26+
fmt.Printf("error: unable to open in browser: \"%s\"\n", err)
2727
os.Exit(1)
2828
}
2929
}

main_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ func TestProcessArgs(t *testing.T) {
88
cases := map[string]struct {
99
args []string
1010
expectedArg string
11-
err string
11+
wantErr bool
1212
}{
1313
"no argument": {
1414
args: []string{"git-open"},
@@ -21,18 +21,18 @@ func TestProcessArgs(t *testing.T) {
2121
"two arguments": {
2222
args: []string{"git-open", "LICENSE", "README.md"},
2323
expectedArg: "",
24-
err: "recieved 2 args, accepts 1",
24+
wantErr: true,
2525
},
2626
}
2727

2828
for name, c := range cases {
2929
t.Run(name, func(t *testing.T) {
3030
arg, err := processArgs(c.args)
3131

32-
if err != nil && c.err == "" {
32+
if err != nil && !c.wantErr {
3333
t.Fatalf("unexpected error:\n\t(GOT): %#v\n\t(WNT): nil", err)
34-
} else if err == nil && len(c.err) > 0 {
35-
t.Fatalf("expected error:\n\t(GOT): nil\n\t(WNT): %s", c.err)
34+
} else if err == nil && c.wantErr {
35+
t.Fatalf("expected error:\n\t(GOT): nil\n")
3636
} else if arg != c.expectedArg {
3737
t.Fatalf("unexpected arg:\n\t(GOT): %#v\n\t(WNT): %#v", arg, c.expectedArg)
3838
}

open/open.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,11 @@ func GetURL(arg string) (string, error) {
6868
return "", err
6969
}
7070

71+
providers := append(DefaultProviders, LoadProviders()...)
72+
7173
// Find the provider by comparing hosts
7274
var p Provider
73-
for _, provider := range DefaultProviders {
75+
for _, provider := range providers {
7476
if strings.Contains(provider.BaseURL, host) {
7577
p = provider
7678
break

open/open_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func TestGetURL(t *testing.T) {
2020
gitdir string
2121
arg string
2222
expectedURL string
23-
err string
23+
wantErr bool
2424
}{
2525
"no argument": {
2626
arg: "",
@@ -73,10 +73,10 @@ func TestGetURL(t *testing.T) {
7373
}
7474

7575
url, err := GetURL(c.arg)
76-
if err != nil && c.err == "" {
76+
if err != nil && !c.wantErr {
7777
t.Fatalf("unexpected error:\n\t(GOT): %#v\n\t(WNT): nil", err)
78-
} else if err == nil && len(c.err) > 0 {
79-
t.Fatalf("expected error:\n\t(GOT): nil\n\t(WNT): %s", c.err)
78+
} else if err == nil && c.wantErr {
79+
t.Fatalf("expected error:\n\t(GOT): nil\n")
8080
} else if url != expectedURL {
8181
t.Fatalf("unexpected url:\n\t(GOT): %#v\n\t(WNT): %#v", url, expectedURL)
8282
}

open/provider.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package open
33
import (
44
"net/url"
55
"strings"
6+
7+
"github.com/ldez/go-git-cmd-wrapper/v2/config"
8+
"github.com/ldez/go-git-cmd-wrapper/v2/git"
69
)
710

811
// DefaultProviders are a list of supported Providers
@@ -50,3 +53,61 @@ func (p Provider) RootURL(repo string) string {
5053
func escapePath(u string) string {
5154
return strings.TrimSuffix(strings.ReplaceAll(url.PathEscape(u), "%2F", "/"), "/")
5255
}
56+
57+
const getRegex = `^open\..*prefix$`
58+
59+
// LoadProviders returns a slice of [Provider] from the global Git config.
60+
//
61+
// The Git config structure includes a base URL as an argument, and commit prefix and path prefix keys and values.
62+
//
63+
// [open "https://git.mydomain.dev"]
64+
// commitprefix = commit
65+
// pathprefix = tree
66+
func LoadProviders() []Provider {
67+
p := []Provider{}
68+
out, _ := git.Config(config.Global, config.GetRegexp(getRegex, ""))
69+
out = strings.TrimSpace(out)
70+
if len(out) == 0 {
71+
return p
72+
}
73+
74+
urls := make(map[string]*struct {
75+
commitPrefix string
76+
pathPrefix string
77+
})
78+
for _, v := range strings.Split(out, "\n") {
79+
s := strings.Split(strings.TrimPrefix(v, "open."), " ")
80+
81+
var value string
82+
if len(s) == 2 {
83+
value = s[1]
84+
}
85+
86+
s = strings.Split(s[0], ".")
87+
key := s[len(s)-1]
88+
url := strings.Join(s[0:len(s)-1], ".")
89+
90+
if urls[url] == nil {
91+
urls[url] = &struct {
92+
commitPrefix string
93+
pathPrefix string
94+
}{}
95+
}
96+
97+
switch key {
98+
case "commitprefix":
99+
urls[url].commitPrefix = value
100+
case "pathprefix":
101+
urls[url].pathPrefix = value
102+
}
103+
}
104+
105+
for k, v := range urls {
106+
p = append(p, Provider{
107+
BaseURL: k,
108+
CommitPrefix: v.commitPrefix,
109+
PathPrefix: v.pathPrefix,
110+
})
111+
}
112+
return p
113+
}

open/provider_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
package open
22

33
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
47
"testing"
8+
9+
"github.com/google/go-cmp/cmp"
10+
"github.com/ldez/go-git-cmd-wrapper/v2/config"
11+
"github.com/ldez/go-git-cmd-wrapper/v2/git"
512
)
613

714
const repo = "arbourd/git-open"
@@ -129,3 +136,71 @@ func TestEscapePath(t *testing.T) {
129136
})
130137
}
131138
}
139+
140+
func TestLoadProviders(t *testing.T) {
141+
gitconfig := filepath.Join(t.TempDir(), ".gitconfig")
142+
_, err := os.Create(gitconfig)
143+
if err != nil {
144+
t.Fatalf("unable create .gitconfig: %s", err)
145+
}
146+
147+
err = os.Setenv("GIT_CONFIG_GLOBAL", gitconfig)
148+
if err != nil {
149+
t.Fatalf("unable to set GIT_CONFIG_GLOBAL: %s", err)
150+
}
151+
152+
cases := map[string]struct {
153+
config []string
154+
expectedProviders []Provider
155+
}{
156+
"empty git config": {
157+
expectedProviders: []Provider{},
158+
},
159+
"single provider": {
160+
config: []string{
161+
"open.https://my.domain.dev.commitprefix -/commit",
162+
"open.https://my.domain.dev.pathprefix -/tree",
163+
},
164+
expectedProviders: []Provider{
165+
{BaseURL: "https://my.domain.dev", CommitPrefix: "-/commit", PathPrefix: "-/tree"},
166+
},
167+
},
168+
"multple providers": {
169+
config: []string{
170+
"open.https://git.example1.dev.commitprefix -/commit",
171+
"open.https://git.example1.dev.pathprefix -/tree",
172+
"open.https://git.example2.dev.commitprefix commit",
173+
"open.https://git.example2.dev.pathprefix tree",
174+
},
175+
expectedProviders: []Provider{
176+
{BaseURL: "https://git.example1.dev", CommitPrefix: "-/commit", PathPrefix: "-/tree"},
177+
{BaseURL: "https://git.example2.dev", CommitPrefix: "commit", PathPrefix: "tree"},
178+
},
179+
},
180+
}
181+
182+
for name, c := range cases {
183+
t.Run(name, func(t *testing.T) {
184+
// removes all `open.https://` Git config entries
185+
out, _ := git.Config(config.Global, config.GetRegexp(getRegex, ""))
186+
for _, v := range strings.Split(strings.TrimSpace(out), "\n") {
187+
key := strings.Split(strings.TrimSpace(v), " ")[0]
188+
189+
git.Config(config.Global, config.Unset(key, ""))
190+
}
191+
192+
for _, v := range c.config {
193+
s := strings.Split(v, " ")
194+
git.Config(config.Global, config.Entry(s[0], s[1]))
195+
}
196+
197+
p := LoadProviders()
198+
if len(p) != len(c.expectedProviders) {
199+
t.Logf("unexpected number of providers\n\t(GOT): %#v\n\t(WNT): %#v", len(p), len(c.expectedProviders))
200+
}
201+
if !cmp.Equal(p, c.expectedProviders) {
202+
t.Fatalf("unexpected providers:\n\t(GOT): %#v\n\t(WNT): %#v", p, c.expectedProviders)
203+
}
204+
})
205+
}
206+
}

0 commit comments

Comments
 (0)