Skip to content

Commit e3ce014

Browse files
ethomsonpaulirish
authored andcommitted
Add support for Visual Studio Team Services and Team Foundation Server (#93)
* Parse remotes as URLs or SCP-style paths Instead of trying to strip on ':' and '/' to simplify a URL, actually switch based on whether the remote path is a URL ("scheme://host:port/path") or an SCP-style path (user@host:path) and parse them separately. This allows us to handle custom ports in HTTP and HTTPS, but ignore custom ports in an SSH url and HTTP remotes, instead of always upgrading them to HTTPS. * Introduce tests for Visual Studio Team Services Add tests for Visual Studio Team Services (VSTS) and Team Foundation Server (TFS). VSTS suggests remote paths in two formats: HTTPS URLs or SSH URLs (including port number). TFS is an on-premises product which - when running as an HTTP endpoint - defaults to port 8080. * Branch selection in Visual Studio Team Services VSTS and TFS URLs end in '/_git/RepositoryName` (with 0 or more leading folders of hierarchy in front of that.) Detect these from the `_git` in the penultimate folder of the path. Append branch information to VSTS and TFS URLs. Since VSTS and TFS use a query string to select a branch, instead of including it in the server path, the `providerBranchRef` for other services was changed to include the leading `/`, and now the `openurl` and `providerBranchRef` are simply concatenated, to avoid an incorrect trailing `/` for the VSTS and TFS branch URLs. * Issues for Visual Studio Team Services Provide issue support for VSTS and TFS, modifying the URL from the `_git` endpoint to the `_workitems` endpoint and appending the `id` query string. * README: add VSTS and TFS support
1 parent 363d054 commit e3ce014

File tree

3 files changed

+109
-16
lines changed

3 files changed

+109
-16
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ git open [remote-name] [branch-name]
1212
git open --issue
1313
```
1414

15-
(`git open` works with these [hosted repo providers](#supported-remote-repositories), `git open --issue` currently only works with GitHub)
15+
(`git open` works with these [hosted repo providers](#supported-remote-repositories), `git open --issue` currently only works with GitHub, Visual Studio Team Services and Team Foundation Server)
1616

1717
### Examples
1818

@@ -100,6 +100,8 @@ git-open can automatically guess the corresponding repository page for remotes
100100
- GitLab custom hosted (see below)
101101
- bitbucket.org
102102
- Atlassian Bitbucket Server (formerly _Atlassian Stash_)
103+
- Visual Studio Team Services
104+
- Team Foundation Server (on-premises)
103105
104106
### GitLab support
105107

git-open

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,37 @@ fi
6262
# ftp[s]://host.xz[:port]/path/to/repo.git/
6363
# [user@]host.xz:path/to/repo.git/ - scp-like but is an alternative to ssh.
6464

65-
# Trim "/" and ".git" from the end of the url
66-
giturl=${giturl%/} giturl=${giturl%.git}
65+
# Determine whether this is a url (https, ssh, git+ssh...) or an scp-style path
66+
if [[ "$giturl" =~ ^[a-z\+]+://.* ]]; then
67+
# Trim URL scheme and possible username
68+
gitprotocol=${giturl%%://*}
69+
uri=${giturl#*://}
70+
uri=${uri#*@}
71+
72+
# Split on first '/ to get server name and path
73+
domain=${uri%%/*}
74+
urlpath=${uri#*/}
75+
76+
# Remove port number from non-http/https protocols (ie, ssh)
77+
if [[ $gitprotocol != 'https' && $gitprotocol != 'http' ]]; then
78+
domain=${domain%:*}
79+
fi
80+
else
81+
# Trim possible username from SSH path
82+
uri=${giturl##*@}
6783

68-
# Trim before last '@' and protocol (*://) from beginning
69-
uri=${giturl##*@} uri=${uri##*://}
84+
# Split on first ':' to get server name and path
85+
domain=${uri%%:*}
86+
urlpath=${uri#*:}
87+
fi
7088

71-
# If there isn't a protocol, we can assume it's using the scp syntax which uses ':' to seperate the path.
72-
[[ $giturl =~ :// ]] && pathsep='/' || pathsep=':'
89+
# Trim "/" from beginning of URL; "/" and ".git" from end of URL
90+
urlpath=${urlpath#/} urlpath=${urlpath%/} urlpath=${urlpath%.git}
7391

74-
# Seperate the domain and the urlpath on the first {pathsep}. This also removes the gitport from the domain.
75-
domain=${uri%%[:$pathsep]*} urlpath=${uri#*$pathsep}
92+
# If the URL is provided as "http", preserve that
93+
if [[ $gitprotocol == 'http' ]]; then
94+
protocol='http'
95+
fi
7696

7797
# Allow config options to replace the server or the protocol
7898
openurl="$protocol://$domain"
@@ -93,30 +113,40 @@ IFS='/' pathargs=($urlpath)
93113

94114
if (( is_issue )); then
95115
# For issues, take the numbers and preprend 'issues/'
96-
providerBranchRef="issues/${branch//[^0-9]/}"
116+
providerBranchRef="/issues/${branch//[^0-9]/}"
97117
else
98118
# Make # and % characters url friendly
99119
# github.com/paulirish/git-open/pull/24
100120
branch=${branch//%/%25} branch=${branch//#/%23}
101-
providerBranchRef="tree/$branch"
121+
providerBranchRef="/tree/$branch"
102122
fi
103123

104124
if [[ "$domain" == 'bitbucket.org' ]]; then
105125
# Bitbucket, see https://github.com/paulirish/git-open/issues/80 for why ?at is needed.
106-
providerBranchRef="src?at=$branch"
126+
providerBranchRef="/src?at=$branch"
107127
elif [[ ${pathargs[0]} == 'scm' ]]; then
108128
# Bitbucket server, which starts with 'scm'
109129
# Replace the first element, 'scm', with 'projects'. Keep the first argument, the string 'repos', and finally the rest of the arguments.
110130
pathargs=('projects' ${pathargs[1]} 'repos' "${pathargs[@]:2}")
111131
IFS='/' urlpath="${pathargs[*]}"
112-
providerBranchRef="browse?at=$branch"
132+
providerBranchRef="/browse?at=$branch"
133+
elif [[ "${#pathargs[@]}" -ge '2' && ${pathargs[${#pathargs[@]} - 2]} == '_git' ]]; then
134+
# Visual Studio Team Services and Team Foundation Server always have /_git/ as the second to last segment in the url path
135+
if (( is_issue )); then
136+
# Switch to workitems, provide work item id if specified
137+
urlpath="${urlpath%%/_git/*}/_workitems"
138+
providerBranchRef="?id=${branch//[^0-9]/}"
139+
else
140+
# Keep project and repository name, append branch selector.
141+
providerBranchRef="?version=GB$branch"
142+
fi
113143
fi
114144

115145
openurl="$protocol://$domain/$urlpath"
116146

117147
# simplify URL for master
118148
if [[ $branch != "master" ]]; then
119-
openurl="$openurl/$providerBranchRef"
149+
openurl="$openurl$providerBranchRef"
120150
fi
121151

122152
# get current open browser command

test/git-open.bats

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,24 @@ setup() {
144144
assert_output "https://github.com/paulirish/git-open"
145145
}
146146

147+
@test "basic: https url can contain port" {
148+
git remote set-url origin "https://github.com:99/user/repo.git"
149+
run ../git-open
150+
assert_output "https://github.com:99/user/repo"
151+
}
152+
153+
@test "basic: ssh url has port removed from http url" {
154+
git remote set-url origin "ssh://github.com:22/user/repo.git"
155+
run ../git-open
156+
assert_output "https://github.com/user/repo"
157+
}
158+
159+
@test "basic: http url scheme is preserved" {
160+
git remote set-url origin "http://github.com/user/repo.git"
161+
run ../git-open
162+
assert_output "http://github.com/user/repo"
163+
}
164+
147165

148166
##
149167
## Bitbucket
@@ -299,10 +317,53 @@ setup() {
299317

300318
git remote set-url origin "https://git.example.com:7000/XXX/YYY.git"
301319
run ../git-open
302-
assert_output "https://git.example.com/XXX/YYY"
303-
refute_output --partial ":7000"
320+
assert_output "https://git.example.com:7000/XXX/YYY"
321+
}
322+
323+
##
324+
## Visual Studio Team Services
325+
##
326+
327+
@test "vsts: https url" {
328+
git remote set-url origin "https://gitopen.visualstudio.com/Project/_git/Repository"
329+
run ../git-open
330+
assert_output --partial "https://gitopen.visualstudio.com/Project/_git/Repository"
304331
}
305332

333+
@test "vsts: ssh url" {
334+
git remote add vsts_ssh "ssh://[email protected]:22/Project/_git/Repository"
335+
run ../git-open "vsts_ssh"
336+
assert_output "https://gitopen.visualstudio.com/Project/_git/Repository"
337+
}
338+
339+
@test "vsts: on-premises tfs http url" {
340+
git remote set-url origin "http://tfs.example.com:8080/Project/_git/Repository"
341+
run ../git-open
342+
assert_output --partial "http://tfs.example.com:8080/Project/_git/Repository"
343+
}
344+
345+
@test "vsts: branch" {
346+
git remote set-url origin "ssh://[email protected]:22/_git/Repository"
347+
git checkout -B "mybranch"
348+
run ../git-open
349+
assert_output "https://gitopen.visualstudio.com/_git/Repository?version=GBmybranch"
350+
}
351+
352+
@test "vsts: on-premises tfs branch" {
353+
git remote set-url origin "http://tfs.example.com:8080/Project/Folder/_git/Repository"
354+
git checkout -B "mybranch"
355+
run ../git-open
356+
assert_output "http://tfs.example.com:8080/Project/Folder/_git/Repository?version=GBmybranch"
357+
}
358+
359+
@test "vsts: issue" {
360+
git remote set-url origin "http://tfs.example.com:8080/Project/Folder/_git/Repository"
361+
git checkout -B "bugfix-36"
362+
run ../git-open "--issue"
363+
assert_output "http://tfs.example.com:8080/Project/Folder/_workitems?id=36"
364+
}
365+
366+
306367
teardown() {
307368
cd ..
308369
rm -rf "$foldername"

0 commit comments

Comments
 (0)