Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 52 additions & 2 deletions git-open
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,51 @@ if [[ -z "$giturl" ]]; then
exit 1
fi

ssh_config=${ssh_config:-"$HOME/.ssh/config"}
# Resolves an ssh alias defined in ssh_config to it's corresponding hostname
# echos out result, should be used within subshell $( ssh_resolve $host )
# echos out nothing if alias could not be resolved
function ssh_resolve() {
domain="$1"
ssh_found=true
# Filter to only ssh_config lines that start with "Host" or "HostName"
resolved=$(while read -r ssh_line; do
# Split each line by spaces, of the form:
# Host alias [alias...]
# Host regex
# HostName resolved.domain.com
read -r -a ssh_array <<<"${ssh_line}"
ssh_optcode="${ssh_array[0]}"
if [[ ${ssh_optcode^^} == HOST ]]; then
# Host
ssh_found=false
# Iterate through aliases looking for a match
for ssh_index in $(seq 1 $((${#ssh_array[@]} - 1))); do
ssh_host=${ssh_array[$ssh_index]}
# shellcheck disable=SC2053
if [[ $domain == $ssh_host ]]; then
# Found a match, next HostName entry will be returned while matched
ssh_found=true
break
fi
done
elif $ssh_found && [[ ${ssh_optcode^^} == HOSTNAME ]]; then
# HostName, but only if ssh_found is true (the last Host entry matched)
# Replace all instances of %h with the Host alias
echo "${ssh_array[1]//%h/$domain}"
fi
done < <(grep -iE "^\\s*Host(Name)?\\s+" "$ssh_config"))
# Take only the last resolved hostname (multiple are overridden)
tail -1 <<<"$resolved"
}

# From git-fetch(5), native protocols:
# ssh://[user@]host.xz[:port]/path/to/repo.git/
# git://host.xz[:port]/path/to/repo.git/
# http[s]://host.xz[:port]/path/to/repo.git/
# ftp[s]://host.xz[:port]/path/to/repo.git/
# [user@]host.xz:path/to/repo.git/ - scp-like but is an alternative to ssh.
# [user@]hostalias:path/to/repo.git/ - handles host aliases defined in ssh_config(5)

# Determine whether this is a url (https, ssh, git+ssh...) or an scp-style path
if [[ "$giturl" =~ ^[a-z\+]+://.* ]]; then
Expand All @@ -85,6 +124,14 @@ else
# Split on first ':' to get server name and path
domain=${uri%%:*}
urlpath=${uri#*:}

# Resolve sshconfig aliases
if [[ -e "$ssh_config" ]]; then
domain_resolv=$(ssh_resolve "$domain")
if [[ ! -z "$domain_resolv" ]]; then
domain="$domain_resolv"
fi
fi
fi

# Trim "/" from beginning of URL; "/" and ".git" from end of URL
Expand All @@ -110,7 +157,7 @@ protocol=$(getConfig "protocol")
branch=${2:-$(git symbolic-ref -q --short HEAD)}

# Split arguments on '/'
IFS='/' pathargs=($urlpath)
IFS='/' read -r -a pathargs <<<"$urlpath"

if (( is_issue )); then
# For issues, take the numbers and preprend 'issues/'
Expand All @@ -133,6 +180,7 @@ elif [[ "${#pathargs[@]}" -ge 3 && ${pathargs[${#pathargs[@]} - 3]} == 'scm' ]];
pathPref=("${pathargs[*]:0:${#pathargs[@]} - 3}")

# Replace the 'scm' element, with 'projects'. Keep the first argument, the string 'repos', and finally the rest of the arguments.
# shellcheck disable=SC2206
pathargs=(${pathPref[@]} 'projects' ${pathargs[${#pathargs[@]} - 2]} 'repos' "${pathargs[@]:${#pathargs[@]} - 1}")
IFS='/' urlpath="${pathargs[*]}"
providerBranchRef="/browse?at=$branch"
Expand Down Expand Up @@ -171,7 +219,9 @@ case $( uname -s ) in
esac

# Allow printing the url if BROWSER=echo
if [[ $BROWSER != "echo" ]]; then
if [[ $BROWSER == "echo" ]]; then
openopt=''
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's this about?

Copy link
Contributor Author

@4U6U57 4U6U57 Dec 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's to fix BATS testing on WSL, which still doesn't work after d97b7f3.

Click to expand test output:
[avalera:~/git-open-clean] master*
± npm test

> [email protected] test /home/avalera/git-open-clean
> npm run unit && npm run lint:package && npm run lint:readme && npm run lint:editorconfig


> [email protected] unit /home/avalera/git-open-clean
> bats test/

 ✓ test environment
 ✓ help text
 ✓ invalid option
 ✗ gh: basic
   (from function `assert_output' in file test/test_helper/bats-assert/src/assert.bash, line 239,
    in test file test/git-open.bats, line 46)
     `assert_output "https://github.com/user/repo"' failed
   Switched to a new branch 'master'
   Reset branch 'master'

   -- output differs --
   expected : https://github.com/user/repo
   actual   : Start https://github.com/user/repo
   --

 ✗ gh: branch
   (from function `assert_output' in file test/test_helper/bats-assert/src/assert.bash, line 239,
    in test file test/git-open.bats, line 53)
     `assert_output "https://github.com/user/repo/tree/mybranch"' failed
   Switched to a new branch 'master'
   Switched to a new branch 'mybranch'

   -- output differs --
   expected : https://github.com/user/repo/tree/mybranch
   actual   : Start https://github.com/user/repo/tree/mybranch
   --

...

The outputted URL's are correct, but prefixed with the word Start from the $openopt variable, causing most tests to fail (ones with --partial still pass, because the correct output is there).

else
exec &>/dev/null
fi

Expand Down
172 changes: 172 additions & 0 deletions test/git-open.bats
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,113 @@ setup() {
assert_output "http://github.com/user/repo"
}

##
## SSH config
##

@test "sshconfig: basic" {
create_ssh_sandbox
# Basic
git remote set-url origin "basic:user/repo.git"
run ../git-open
assert_output --partial "https://basic.com/user/repo"
# With git user
git remote set-url origin "git@nouser:user/repo.git"
run ../git-open
assert_output "https://no.user/user/repo"
}

@test "sshconfig: no action on no match" {
create_ssh_sandbox
git remote set-url origin "git@nomatch:user/repo.git"
run ../git-open
assert_output "https://nomatch/user/repo"
# No match due to improper casing
}

@test "sshconfig: check case sensitivity" {
create_ssh_sandbox
# Host and HostName keywords should be case insensitive
# But output URL will be case sensitive
git remote set-url origin "malformed:user/repo.git"
run ../git-open
assert_output "https://MaL.FoRmEd/user/repo"
# SSH aliases (hosts) are case sensitive, this should not match
git remote set-url origin "git@MALFORMED:user/repo.git"
run ../git-open
refute_output "https://MaL.FoRmEd/user/repo"
}

@test "sshconfig: multitarget host" {
create_ssh_sandbox
for i in $(seq 1 3); do
git remote set-url origin "multi$i:user/repo.git"
run ../git-open
assert_output "https://multi.com/user/repo"
done
}

@test "sshconfig: host substitution in hostname" {
create_ssh_sandbox
for i in $(seq 1 3); do
git remote set-url origin "sub$i:user/repo.git"
run ../git-open
assert_output "https://sub$i.multi.com/user/repo"
done
}

@test "sshconfig: host wildcard * matches zero or more chars" {
create_ssh_sandbox
# Normal *
for str in "" "-prod" "-dev"; do
git remote set-url origin "zero$str:user/repo.git"
run ../git-open
assert_output "https://zero.com/user/repo"
done
# * with substitution
for str in "" "-prod" "-dev"; do
git remote set-url origin "subzero$str:user/repo.git"
run ../git-open
assert_output "https://subzero$str.zero/user/repo"
done
}

@test "sshconfig: host wildcard ? matches exactly one char" {
create_ssh_sandbox
# Normal ?
for i in $(seq 1 3); do
git remote set-url origin "one$i:user/repo.git"
run ../git-open
assert_output "https://one.com/user/repo"
done
# Refute invalid match on ?
for str in "" "-test"; do
git remote set-url origin "one:user/repo.git"
run ../git-open
refute_output "https://one$str.com/user/repo"
done

# ? with substitution
for i in $(seq 1 3); do
git remote set-url origin "subone$i:user/repo.git"
run ../git-open
assert_output "https://subone$i.one/user/repo"
done
# Refute invalid match on ? with substitution
for str in "" "-test"; do
git remote set-url origin "subone$str:user/repo.git"
run ../git-open
refute_output "https://subone$str.one/user/repo"
done
# Refute invalid match on ? with substitution
}

@test "sshconfig: overriding host rules" {
create_ssh_sandbox
git remote set-url origin "zero-override:user/repo.git"
run ../git-open
assert_output "https://override.zero.com/user/repo"
}

##
## Bitbucket
Expand Down Expand Up @@ -393,6 +500,9 @@ setup() {
teardown() {
cd ..
rm -rf "$foldername"
rm -rf "$ssh_config"
refute [ -e "$ssh_config" ]
unset ssh_config
}

# helper to create a test git sandbox that won't dirty the real repo
Expand All @@ -418,3 +528,65 @@ function create_git_sandbox() {
git add readme.txt
git commit -m "add file" -q
}

# helper to create test SSH config file
function create_ssh_sandbox() {
export ssh_config=$(mktemp)
refute [ -z "$ssh_config" ]

# Populate ssh config with test data
echo "$ssh_testdata" >$ssh_config
assert [ -e "$ssh_config" ]
}

# Test SSH config data
ssh_testdata="
# Autogenerated test sshconfig for paulirish/git-open BATS tests
# It is safe to delete this file, a new one will be generated each test

Host basic
HostName basic.com
User git

Host nomatch
User git

Host nouser
HostName no.user

host malformed
hOsTnAmE MaL.FoRmEd
User other

# Multiple targets
Host multi1 multi2 multi3
HostName multi.com
User git

Host sub1 sub2 sub3
HostName %h.multi.com
User git

# Wildcard * matching (zero or more characters)
Host zero*
HostName zero.com
User git

Host subzero*
HostName %h.zero
User git

# Wildcard ? matching (exactly one character)
Host one?
HostName one.com
User git

Host subone?
HostName %h.one
User git

# Overrides rule zero*
Host zero-override
HostName override.zero.com
User git
"